Merge pull request #1419 from ZLMediaKit/feature/mmap

通过共享mmap方式提高http文件/hls服务器性能
This commit is contained in:
夏楚 2022-02-11 16:56:08 +08:00 committed by GitHub
commit d432888ad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 589 additions and 447 deletions

View File

@ -76,15 +76,17 @@ void HlsPlayer::fetchSegment() {
} }
return Socket::createSocket(poller, true); return Socket::createSocket(poller, true);
}); });
auto benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
_http_ts_player->setOnPacket([weak_self](const char *data, size_t len) { if (!benchmark_mode) {
auto strong_self = weak_self.lock(); _http_ts_player->setOnPacket([weak_self](const char *data, size_t len) {
if (!strong_self) { auto strong_self = weak_self.lock();
return; if (!strong_self) {
} return;
//收到ts包 }
strong_self->onPacket_l(data, len); //收到ts包
}); strong_self->onPacket_l(data, len);
});
}
if (!(*this)[Client::kNetAdapter].empty()) { if (!(*this)[Client::kNetAdapter].empty()) {
_http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]); _http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]);
@ -349,7 +351,8 @@ void HlsPlayerImp::addTrackCompleted() {
} }
void HlsPlayerImp::onPlayResult(const SockException &ex) { void HlsPlayerImp::onPlayResult(const SockException &ex) {
if (ex) { auto benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
if (ex || benchmark_mode) {
PlayerImp<HlsPlayer, PlayerBase>::onPlayResult(ex); PlayerImp<HlsPlayer, PlayerBase>::onPlayResult(ex);
} else { } else {
auto demuxer = std::make_shared<HlsDemuxer>(); auto demuxer = std::make_shared<HlsDemuxer>();
@ -364,6 +367,9 @@ void HlsPlayerImp::onShutdown(const SockException &ex) {
} }
vector<Track::Ptr> HlsPlayerImp::getTracks(bool ready) const { vector<Track::Ptr> HlsPlayerImp::getTracks(bool ready) const {
if (!_demuxer) {
return vector<Track::Ptr>();
}
return static_pointer_cast<HlsDemuxer>(_demuxer)->getTracks(ready); return static_pointer_cast<HlsDemuxer>(_demuxer)->getTracks(ready);
} }

View File

@ -9,16 +9,24 @@
*/ */
#include <csignal> #include <csignal>
#include "HttpBody.h" #include <tuple>
#include "Util/util.h"
#include "Util/File.h"
#include "Util/uv_errno.h"
#include "Util/logger.h"
#include "Util/onceToken.h"
#include "HttpClient.h"
#ifndef _WIN32 #ifndef _WIN32
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#if defined(__linux__) || defined(__linux)
#include <sys/sendfile.h>
#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 #ifndef _WIN32
#define ENABLE_MMAP #define ENABLE_MMAP
@ -29,94 +37,146 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
HttpStringBody::HttpStringBody(string str){ HttpStringBody::HttpStringBody(string str) {
_str = std::move(str); _str = std::move(str);
} }
ssize_t HttpStringBody::remainSize() { int64_t HttpStringBody::remainSize() {
return _str.size() - _offset; return _str.size() - _offset;
} }
Buffer::Ptr HttpStringBody::readData(size_t size) { Buffer::Ptr HttpStringBody::readData(size_t size) {
size = MIN((size_t)remainSize(), size); size = MIN((size_t)remainSize(), size);
if(!size){ if (!size) {
//没有剩余字节了 //没有剩余字节了
return nullptr; return nullptr;
} }
auto ret = std::make_shared<BufferString>(_str,_offset,size); auto ret = std::make_shared<BufferString>(_str, _offset, size);
_offset += size; _offset += size;
return ret; return ret;
} }
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
HttpFileBody::HttpFileBody(const string &filePath, bool use_mmap) { #ifdef ENABLE_MMAP
std::shared_ptr<FILE> fp(fopen(filePath.data(), "rb"), [](FILE *fp) {
static mutex s_mtx;
static unordered_map<string /*file_path*/, std::tuple<char */*ptr*/, int64_t /*size*/, weak_ptr<char> /*mmap*/ > > s_shared_mmap;
//删除mmap记录
static void delSharedMmap(const string &file_path, char *ptr) {
lock_guard<mutex> 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<char> getSharedMmap(const string &file_path, int64_t &file_size) {
{
lock_guard<mutex> 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<FILE> fp(fopen(file_path.data(), "rb"), [](FILE *fp) {
if (fp) { if (fp) {
fclose(fp); fclose(fp);
} }
}); });
if (!fp) { if (!fp) {
init(fp, 0, 0, use_mmap); //文件不存在
} else { file_size = -1;
init(fp, 0, File::fileSize(fp.get()), use_mmap); 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<char> 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<mutex> 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<FILE> &fp, size_t offset, size_t max_size, bool use_mmap) { void HttpFileBody::setRange(uint64_t offset, uint64_t max_size) {
init(fp, offset, max_size, use_mmap); 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 <sys/sendfile.h>
#endif
int HttpFileBody::sendFile(int fd) { int HttpFileBody::sendFile(int fd) {
#if defined(__linux__) || defined(__linux) #if defined(__linux__) || defined(__linux)
static onceToken s_token([]() { if (!_fp) {
signal(SIGPIPE, SIG_IGN); return -1;
}); }
static onceToken s_token([]() { signal(SIGPIPE, SIG_IGN); });
off_t off = _file_offset; 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 #else
return -1; return -1;
#endif #endif
} }
void HttpFileBody::init(const std::shared_ptr<FILE> &fp, size_t offset, size_t max_size, bool use_mmap) { class BufferMmap : public Buffer {
_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{
public: public:
typedef std::shared_ptr<BufferMmap> Ptr; typedef std::shared_ptr<BufferMmap> Ptr;
BufferMmap(const std::shared_ptr<char> &map_addr, size_t offset, size_t size) { BufferMmap(const std::shared_ptr<char> &map_addr, size_t offset, size_t size) {
@ -124,103 +184,96 @@ public:
_data = map_addr.get() + offset; _data = map_addr.get() + offset;
_size = size; _size = size;
} }
~BufferMmap() override{}; ~BufferMmap() override {};
//返回数据长度 //返回数据长度
char *data() const override { char *data() const override { return _data; }
return _data; size_t size() const override { return _size; }
}
size_t size() const override{
return _size;
}
private: private:
std::shared_ptr<char> _map_addr;
char *_data; char *_data;
size_t _size; size_t _size;
std::shared_ptr<char> _map_addr;
}; };
ssize_t HttpFileBody::remainSize() { int64_t HttpFileBody::remainSize() {
return _max_size - _offset; return _read_to - _file_offset;
} }
Buffer::Ptr HttpFileBody::readData(size_t size) { Buffer::Ptr HttpFileBody::readData(size_t size) {
size = MIN((size_t)remainSize(),size); size = MIN((size_t)remainSize(), size);
if(!size){ if (!size) {
//没有剩余字节了 //没有剩余字节了
return nullptr; return nullptr;
} }
if(!_map_addr){ if (!_map_addr) {
//fread模式 // fread模式
ssize_t iRead; ssize_t iRead;
auto ret = _pool.obtain2(); auto ret = _pool.obtain2();
ret->setCapacity(size + 1); ret->setCapacity(size + 1);
do{ do {
iRead = fread(ret->data(), 1, size, _fp.get()); 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); ret->setSize(iRead);
_offset += iRead; _file_offset += iRead;
return std::move(ret); return std::move(ret);
} }
//读取文件异常,文件真实长度小于声明长度 //读取文件异常,文件真实长度小于声明长度
_offset = _max_size; _file_offset = _read_to;
WarnL << "read file err:" << get_uv_errmsg(); WarnL << "read file err:" << get_uv_errmsg();
return nullptr; return nullptr;
} }
//mmap模式 // mmap模式
auto ret = std::make_shared<BufferMmap>(_map_addr,_offset,size); auto ret = std::make_shared<BufferMmap>(_map_addr, _file_offset, size);
_offset += size; _file_offset += size;
return ret; return ret;
} }
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
HttpMultiFormBody::HttpMultiFormBody(const HttpArgs &args,const string &filePath,const string &boundary){
std::shared_ptr<FILE> fp(fopen(filePath.data(), "rb"), [](FILE *fp) { HttpMultiFormBody::HttpMultiFormBody(const HttpArgs &args, const string &filePath, const string &boundary) {
if(fp){ _fileBody = std::make_shared<HttpFileBody>(filePath);
fclose(fp); if (_fileBody->remainSize() < 0) {
}
});
if(!fp){
throw std::invalid_argument(StrPrinter << "open file failed" << filePath << " " << get_uv_errmsg()); throw std::invalid_argument(StrPrinter << "open file failed" << filePath << " " << get_uv_errmsg());
} }
_fileBody = std::make_shared<HttpFileBody>(fp, 0, File::fileSize(fp.get()));
auto fileName = filePath; auto fileName = filePath;
auto pos = filePath.rfind('/'); auto pos = filePath.rfind('/');
if(pos != string::npos){ if (pos != string::npos) {
fileName = filePath.substr(pos + 1); fileName = filePath.substr(pos + 1);
} }
_bodyPrefix = multiFormBodyPrefix(args,boundary,fileName); _bodyPrefix = multiFormBodyPrefix(args, boundary, fileName);
_bodySuffix = multiFormBodySuffix(boundary); _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; return _totalSize - _offset;
} }
Buffer::Ptr HttpMultiFormBody::readData(size_t size){ Buffer::Ptr HttpMultiFormBody::readData(size_t size) {
if(_bodyPrefix.size()){ if (_bodyPrefix.size()) {
auto ret = std::make_shared<BufferString>(_bodyPrefix); auto ret = std::make_shared<BufferString>(_bodyPrefix);
_offset += _bodyPrefix.size(); _offset += _bodyPrefix.size();
_bodyPrefix.clear(); _bodyPrefix.clear();
return ret; return ret;
} }
if(_fileBody->remainSize()){ if (_fileBody->remainSize()) {
auto ret = _fileBody->readData(size); auto ret = _fileBody->readData(size);
if(!ret){ if (!ret) {
//读取文件出现异常,提前中断 //读取文件出现异常,提前中断
_offset = _totalSize; _offset = _totalSize;
}else{ } else {
_offset += ret->size(); _offset += ret->size();
} }
return ret; return ret;
} }
if(_bodySuffix.size()){ if (_bodySuffix.size()) {
auto ret = std::make_shared<BufferString>(_bodySuffix); auto ret = std::make_shared<BufferString>(_bodySuffix);
_offset = _totalSize; _offset = _totalSize;
_bodySuffix.clear(); _bodySuffix.clear();
@ -230,7 +283,7 @@ Buffer::Ptr HttpMultiFormBody::readData(size_t size){
return nullptr; return nullptr;
} }
string HttpMultiFormBody::multiFormBodySuffix(const string &boundary){ string HttpMultiFormBody::multiFormBodySuffix(const string &boundary) {
string MPboundary = string("--") + boundary; string MPboundary = string("--") + boundary;
string endMPboundary = MPboundary + "--"; string endMPboundary = MPboundary + "--";
_StrPrinter body; _StrPrinter body;
@ -238,21 +291,23 @@ string HttpMultiFormBody::multiFormBodySuffix(const string &boundary){
return std::move(body); return std::move(body);
} }
string HttpMultiFormBody::multiFormContentType(const string &boundary){ string HttpMultiFormBody::multiFormContentType(const string &boundary) {
return StrPrinter << "multipart/form-data; boundary=" << 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; string MPboundary = string("--") + boundary;
_StrPrinter body; _StrPrinter body;
for(auto &pr : args){ for (auto &pr : args) {
body << MPboundary << "\r\n"; body << MPboundary << "\r\n";
body << "Content-Disposition: form-data; name=\"" << pr.first << "\"\r\n\r\n"; body << "Content-Disposition: form-data; name=\"" << pr.first << "\"\r\n\r\n";
body << pr.second << "\r\n"; body << pr.second << "\r\n";
} }
body << MPboundary << "\r\n"; body << MPboundary << "\r\n";
body << "Content-Disposition: form-data; name=\"" << "file" << "\";filename=\"" << fileName << "\"\r\n"; body << "Content-Disposition: form-data; name=\""
body << "Content-Type: application/octet-stream\r\n\r\n" ; << "file"
<< "\";filename=\"" << fileName << "\"\r\n";
body << "Content-Type: application/octet-stream\r\n\r\n";
return std::move(body); return std::move(body);
} }
@ -260,7 +315,7 @@ HttpBufferBody::HttpBufferBody(Buffer::Ptr buffer) {
_buffer = std::move(buffer); _buffer = std::move(buffer);
} }
ssize_t HttpBufferBody::remainSize() { int64_t HttpBufferBody::remainSize() {
return _buffer ? _buffer->size() : 0; return _buffer ? _buffer->size() : 0;
} }
@ -268,4 +323,4 @@ Buffer::Ptr HttpBufferBody::readData(size_t size) {
return Buffer::Ptr(std::move(_buffer)); return Buffer::Ptr(std::move(_buffer));
} }
}//namespace mediakit } // namespace mediakit

View File

@ -37,7 +37,7 @@ public:
/** /**
* -1, content-length * -1, content-length
*/ */
virtual ssize_t remainSize() { return 0;}; virtual int64_t remainSize() { return 0;};
/** /**
* size * size
@ -77,7 +77,7 @@ public:
HttpStringBody(std::string str); HttpStringBody(std::string str);
~HttpStringBody() override = default; ~HttpStringBody() override = default;
ssize_t remainSize() override; int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override ; toolkit::Buffer::Ptr readData(size_t size) override ;
private: private:
@ -94,7 +94,7 @@ public:
HttpBufferBody(toolkit::Buffer::Ptr buffer); HttpBufferBody(toolkit::Buffer::Ptr buffer);
~HttpBufferBody() override = default; ~HttpBufferBody() override = default;
ssize_t remainSize() override; int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override; toolkit::Buffer::Ptr readData(size_t size) override;
private: private:
@ -104,32 +104,32 @@ private:
/** /**
* content * content
*/ */
class HttpFileBody : public HttpBody{ class HttpFileBody : public HttpBody {
public: public:
typedef std::shared_ptr<HttpFileBody> Ptr; typedef std::shared_ptr<HttpFileBody> Ptr;
/** /**
* *
* @param fp 0 * @param file_path
* @param offset
* @param max_size
* @param use_mmap 使mmap方式访问文件 * @param use_mmap 使mmap方式访问文件
*/ */
HttpFileBody(const std::shared_ptr<FILE> &fp, size_t offset, size_t max_size, bool use_mmap = true);
HttpFileBody(const std::string &file_path, bool use_mmap = true); HttpFileBody(const std::string &file_path, bool use_mmap = true);
~HttpFileBody() override = default; ~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; toolkit::Buffer::Ptr readData(size_t size) override;
int sendFile(int fd) override; int sendFile(int fd) override;
private: private:
void init(const std::shared_ptr<FILE> &fp,size_t offset,size_t max_size, bool use_mmap); int64_t _read_to = 0;
uint64_t _file_offset = 0;
private:
size_t _max_size;
size_t _offset = 0;
size_t _file_offset = 0;
std::shared_ptr<FILE> _fp; std::shared_ptr<FILE> _fp;
std::shared_ptr<char> _map_addr; std::shared_ptr<char> _map_addr;
toolkit::ResourcePool<toolkit::BufferRaw> _pool; toolkit::ResourcePool<toolkit::BufferRaw> _pool;
@ -152,7 +152,7 @@ public:
*/ */
HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY"); HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY");
virtual ~HttpMultiFormBody(){} virtual ~HttpMultiFormBody(){}
ssize_t remainSize() override ; int64_t remainSize() override ;
toolkit::Buffer::Ptr readData(size_t size) override; toolkit::Buffer::Ptr readData(size_t size) override;
public: public:
@ -161,8 +161,8 @@ public:
static std::string multiFormContentType(const std::string &boundary); static std::string multiFormContentType(const std::string &boundary);
private: private:
size_t _offset = 0; uint64_t _offset = 0;
size_t _totalSize; int64_t _totalSize;
std::string _bodyPrefix; std::string _bodyPrefix;
std::string _bodySuffix; std::string _bodySuffix;
HttpFileBody::Ptr _fileBody; HttpFileBody::Ptr _fileBody;

View File

@ -10,7 +10,7 @@
#include "HttpCookie.h" #include "HttpCookie.h"
#include "Util/util.h" #include "Util/util.h"
#include "Util/logger.h" #include "Util/onceToken.h"
#if defined(_WIN32) #if defined(_WIN32)
#include "Util/strptime_win.h" #include "Util/strptime_win.h"
@ -21,28 +21,75 @@ using namespace std;
namespace mediakit { namespace mediakit {
void HttpCookie::setPath(const string &path){ void HttpCookie::setPath(const string &path) {
_path = path; _path = path;
} }
void HttpCookie::setHost(const string &host){
void HttpCookie::setHost(const string &host) {
_host = host; _host = host;
} }
static time_t timeStrToInt(const string &date){
struct tm tt; static long s_gmtoff = 0; //时间差
strptime(date.data(),"%a, %b %d %Y %H:%M:%S %Z",&tt); static onceToken s_token([]() {
return mktime(&tt); #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); _expire = timeStrToInt(expires);
if(!server_date.empty()){ if (!server_date.empty()) {
_expire = time(NULL) + (_expire - timeStrToInt(server_date)); _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; _key = key;
_val = val; _val = val;
} }
HttpCookie::operator bool (){
HttpCookie::operator bool() {
return !_host.empty() && !_key.empty() && !_val.empty() && (_expire > time(NULL)); return !_host.empty() && !_key.empty() && !_val.empty() && (_expire > time(NULL));
} }
@ -50,19 +97,18 @@ const string &HttpCookie::getVal() const {
return _val; return _val;
} }
const string &HttpCookie::getKey() const{ const string &HttpCookie::getKey() const {
return _key; return _key;
} }
HttpCookieStorage &HttpCookieStorage::Instance() {
HttpCookieStorage &HttpCookieStorage::Instance(){
static HttpCookieStorage instance; static HttpCookieStorage instance;
return instance; return instance;
} }
void HttpCookieStorage::set(const HttpCookie::Ptr &cookie) { void HttpCookieStorage::set(const HttpCookie::Ptr &cookie) {
lock_guard<mutex> lck(_mtx_cookie); lock_guard<mutex> lck(_mtx_cookie);
if(!cookie || !(*cookie)){ if (!cookie || !(*cookie)) {
return; return;
} }
_all_cookie[cookie->_host][cookie->_path][cookie->_key] = cookie; _all_cookie[cookie->_host][cookie->_path][cookie->_key] = cookie;
@ -71,20 +117,20 @@ void HttpCookieStorage::set(const HttpCookie::Ptr &cookie) {
vector<HttpCookie::Ptr> HttpCookieStorage::get(const string &host, const string &path) { vector<HttpCookie::Ptr> HttpCookieStorage::get(const string &host, const string &path) {
vector<HttpCookie::Ptr> ret(0); vector<HttpCookie::Ptr> ret(0);
lock_guard<mutex> lck(_mtx_cookie); lock_guard<mutex> lck(_mtx_cookie);
auto it = _all_cookie.find(host); auto it = _all_cookie.find(host);
if(it == _all_cookie.end()){ if (it == _all_cookie.end()) {
//未找到该host相关记录 //未找到该host相关记录
return ret; return ret;
} }
//遍历该host下所有path //遍历该host下所有path
for(auto &pr : it->second){ for (auto &pr : it->second) {
if(path.find(pr.first) != 0){ if (path.find(pr.first) != 0) {
//这个path不匹配 //这个path不匹配
continue; continue;
} }
//遍历该path下的各个cookie //遍历该path下的各个cookie
for(auto it_cookie = pr.second.begin() ; it_cookie != pr.second.end() ; ){ for (auto it_cookie = pr.second.begin(); it_cookie != pr.second.end();) {
if(!*(it_cookie->second)){ if (!*(it_cookie->second)) {
//该cookie已经过期移除之 //该cookie已经过期移除之
it_cookie = pr.second.erase(it_cookie); it_cookie = pr.second.erase(it_cookie);
continue; continue;
@ -97,5 +143,4 @@ vector<HttpCookie::Ptr> HttpCookieStorage::get(const string &host, const string
return ret; return ret;
} }
} /* namespace mediakit */ } /* namespace mediakit */

View File

@ -8,10 +8,10 @@
* may be found in the AUTHORS file in the root of the source tree. * 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 "HttpCookieManager.h"
#include "Common/config.h"
#include "Util/MD5.h"
#include "Util/util.h"
using namespace std; using namespace std;
using namespace toolkit; using namespace toolkit;
@ -19,27 +19,25 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
//////////////////////////////HttpServerCookie//////////////////////////////////// //////////////////////////////HttpServerCookie////////////////////////////////////
HttpServerCookie::HttpServerCookie(const std::shared_ptr<HttpCookieManager> &manager, HttpServerCookie::HttpServerCookie(
const string &cookie_name, const std::shared_ptr<HttpCookieManager> &manager, const string &cookie_name, const string &uid,
const string &uid, const string &cookie, uint64_t max_elapsed) {
const string &cookie,
uint64_t max_elapsed){
_uid = uid; _uid = uid;
_max_elapsed = max_elapsed; _max_elapsed = max_elapsed;
_cookie_uuid = cookie; _cookie_uuid = cookie;
_cookie_name = cookie_name; _cookie_name = cookie_name;
_manager = manager; _manager = manager;
manager->onAddCookie(_cookie_name,_uid,_cookie_uuid); manager->onAddCookie(_cookie_name, _uid, _cookie_uuid);
} }
HttpServerCookie::~HttpServerCookie() { HttpServerCookie::~HttpServerCookie() {
auto strongManager = _manager.lock(); auto strongManager = _manager.lock();
if(strongManager){ if (strongManager) {
strongManager->onDelCookie(_cookie_name,_uid,_cookie_uuid); strongManager->onDelCookie(_cookie_name, _uid, _cookie_uuid);
} }
} }
const string & HttpServerCookie::getUid() const{ const string &HttpServerCookie::getUid() const {
return _uid; return _uid;
} }
@ -47,11 +45,11 @@ string HttpServerCookie::getCookie(const string &path) const {
return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path); return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path);
} }
const string& HttpServerCookie::getCookie() const { const string &HttpServerCookie::getCookie() const {
return _cookie_uuid; return _cookie_uuid;
} }
const string& HttpServerCookie::getCookieName() const{ const string &HttpServerCookie::getCookieName() const {
return _cookie_name; return _cookie_name;
} }
@ -63,11 +61,11 @@ bool HttpServerCookie::isExpired() {
return _ticker.elapsedTime() > _max_elapsed * 1000; return _ticker.elapsedTime() > _max_elapsed * 1000;
} }
std::shared_ptr<lock_guard<recursive_mutex> > HttpServerCookie::getLock(){ void HttpServerCookie::setAttach(std::shared_ptr<void> attach) {
return std::make_shared<lock_guard<recursive_mutex> >(_mtx); _attach = std::move(attach);
} }
string HttpServerCookie::cookieExpireTime() const{ string HttpServerCookie::cookieExpireTime() const {
char buf[64]; char buf[64];
time_t tt = time(NULL) + _max_elapsed; time_t tt = time(NULL) + _max_elapsed;
strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt)); strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
@ -78,10 +76,13 @@ INSTANCE_IMP(HttpCookieManager);
HttpCookieManager::HttpCookieManager() { HttpCookieManager::HttpCookieManager() {
//定时删除过期的cookie防止内存膨胀 //定时删除过期的cookie防止内存膨胀
_timer = std::make_shared<Timer>(10.0f,[this](){ _timer = std::make_shared<Timer>(
onManager(); 10.0f,
return true; [this]() {
}, nullptr); onManager();
return true;
},
nullptr);
} }
HttpCookieManager::~HttpCookieManager() { HttpCookieManager::~HttpCookieManager() {
@ -91,11 +92,11 @@ HttpCookieManager::~HttpCookieManager() {
void HttpCookieManager::onManager() { void HttpCookieManager::onManager() {
lock_guard<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> 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 //再遍历该类型下的所有cookie
for (auto it_cookie = it_name->second.begin() ; it_cookie != it_name->second.end() ; ){ for (auto it_cookie = it_name->second.begin(); it_cookie != it_name->second.end();) {
if(it_cookie->second->isExpired()){ if (it_cookie->second->isExpired()) {
//cookie过期,移除记录 // cookie过期,移除记录
DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie(); DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie();
it_cookie = it_name->second.erase(it_cookie); it_cookie = it_name->second.erase(it_cookie);
continue; continue;
@ -103,7 +104,7 @@ void HttpCookieManager::onManager() {
++it_cookie; ++it_cookie;
} }
if(it_name->second.empty()){ if (it_name->second.empty()) {
//该类型下没有任何cooki记录,移除之 //该类型下没有任何cooki记录,移除之
DebugL << "该path下没有任何cooki记录:" << it_name->first; DebugL << "该path下没有任何cooki记录:" << it_name->first;
it_name = _map_cookie.erase(it_name); 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<void> attach, int max_client) {
lock_guard<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
auto cookie = _geneator.obtain(); auto cookie = _geneator.obtain();
auto uid = uidIn.empty() ? cookie : uidIn; auto uid = uid_in.empty() ? cookie : uid_in;
auto oldCookie = getOldestCookie(cookie_name , uid, max_client); auto oldCookie = getOldestCookie(cookie_name, uid, max_client);
if(!oldCookie.empty()){ if (!oldCookie.empty()) {
//假如该账号已经登录了那么删除老的cookie。 //假如该账号已经登录了那么删除老的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 //保存该账号下的新cookie
_map_cookie[cookie_name][cookie] = data; _map_cookie[cookie_name][cookie] = data;
return 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<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
auto it_name = _map_cookie.find(cookie_name); auto it_name = _map_cookie.find(cookie_name);
if(it_name == _map_cookie.end()){ if (it_name == _map_cookie.end()) {
//不存在该类型的cookie //不存在该类型的cookie
return nullptr; return nullptr;
} }
auto it_cookie = it_name->second.find(cookie); auto it_cookie = it_name->second.find(cookie);
if(it_cookie == it_name->second.end()){ if (it_cookie == it_name->second.end()) {
//该类型下没有对应的cookie //该类型下没有对应的cookie
return nullptr; return nullptr;
} }
if(it_cookie->second->isExpired()){ if (it_cookie->second->isExpired()) {
//cookie过期 // cookie过期
DebugL << "cookie过期:" << it_cookie->second->getCookie(); DebugL << "cookie过期:" << it_cookie->second->getCookie();
it_name->second.erase(it_cookie); it_name->second.erase(it_cookie);
return nullptr; return nullptr;
@ -150,7 +153,7 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,con
return it_cookie->second; 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"); auto it = http_header.find("Cookie");
if (it == http_header.end()) { if (it == http_header.end()) {
return nullptr; return nullptr;
@ -159,100 +162,100 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,con
if (!cookie.size()) { if (!cookie.size()) {
cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr); cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr);
} }
if(cookie.empty()){ if (cookie.empty()) {
return nullptr; 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){ HttpServerCookie::Ptr HttpCookieManager::getCookieByUid(const string &cookie_name, const string &uid) {
if(cookie_name.empty() || uid.empty()){ if (cookie_name.empty() || uid.empty()) {
return nullptr; return nullptr;
} }
auto cookie = getOldestCookie(cookie_name,uid); auto cookie = getOldestCookie(cookie_name, uid);
if(cookie.empty()){ if (cookie.empty()) {
return nullptr; return nullptr;
} }
return getCookie(cookie_name,cookie); return getCookie(cookie_name, cookie);
} }
bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) { bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) {
if(!cookie){ if (!cookie) {
return false; 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<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
auto it_name = _map_cookie.find(cookie_name); auto it_name = _map_cookie.find(cookie_name);
if(it_name == _map_cookie.end()){ if (it_name == _map_cookie.end()) {
return false; return false;
} }
return it_name->second.erase(cookie); 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目的是实现单账号多地登录时挤占登录 //添加新的cookie我们记录下这个uid下有哪些cookie目的是实现单账号多地登录时挤占登录
lock_guard<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
//相同用户下可以存在多个cookie(意味多地登录)这些cookie根据登录时间的早晚依次排序 //相同用户下可以存在多个cookie(意味多地登录)这些cookie根据登录时间的早晚依次排序
_map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = 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<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
//回收随机字符串 //回收随机字符串
_geneator.release(cookie); _geneator.release(cookie);
auto it_name = _map_uid_to_cookie.find(cookie_name); 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; return;
} }
auto it_uid = it_name->second.find(uid); auto it_uid = it_name->second.find(uid);
if(it_uid == it_name->second.end()){ if (it_uid == it_name->second.end()) {
//该用户尚未登录 //该用户尚未登录
return; return;
} }
//遍历同一名用户下的所有客户端,移除命中的客户端 //遍历同一名用户下的所有客户端,移除命中的客户端
for(auto it_cookie = it_uid->second.begin() ; it_cookie != it_uid->second.end() ; ++it_cookie ){ for (auto it_cookie = it_uid->second.begin(); it_cookie != it_uid->second.end(); ++it_cookie) {
if(it_cookie->second != cookie) { if (it_cookie->second != cookie) {
//不是该cookie //不是该cookie
continue; continue;
} }
//移除该用户名下的某个cookie这个设备cookie将失效 //移除该用户名下的某个cookie这个设备cookie将失效
it_uid->second.erase(it_cookie); it_uid->second.erase(it_cookie);
if(it_uid->second.size() != 0) { if (it_uid->second.size() != 0) {
break; break;
} }
//该用户名下没有任何设备在线,移除之 //该用户名下没有任何设备在线,移除之
it_name->second.erase(it_uid); it_name->second.erase(it_uid);
if(it_name->second.size() != 0) { if (it_name->second.size() != 0) {
break; break;
} }
//该类型下未有任何用户在线,移除之 //该类型下未有任何用户在线,移除之
_map_uid_to_cookie.erase(it_name); _map_uid_to_cookie.erase(it_name);
break; 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<recursive_mutex> lck(_mtx_cookie); lock_guard<recursive_mutex> lck(_mtx_cookie);
auto it_name = _map_uid_to_cookie.find(cookie_name); 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 //不存在该类型的cookie
return ""; return "";
} }
auto it_uid = it_name->second.find(uid); auto it_uid = it_name->second.find(uid);
if(it_uid == it_name->second.end()){ if (it_uid == it_name->second.end()) {
//该用户从未登录过 //该用户从未登录过
return ""; return "";
} }
if(it_uid->second.size() < MAX(1,max_client)){ if (it_uid->second.size() < MAX(1, max_client)) {
//同一名用户下,客户端个数还没达到限制个数 //同一名用户下,客户端个数还没达到限制个数
return ""; return "";
} }
@ -261,28 +264,29 @@ string HttpCookieManager::getOldestCookie(const string &cookie_name,const string
} }
/////////////////////////////////RandStrGeneator//////////////////////////////////// /////////////////////////////////RandStrGeneator////////////////////////////////////
string RandStrGeneator::obtain(){ string RandStrGeneator::obtain() {
//获取唯一的防膨胀的随机字符串 //获取唯一的防膨胀的随机字符串
while (true){ while (true) {
auto str = obtain_l(); auto str = obtain_l();
if(_obtained.find(str) == _obtained.end()){ if (_obtained.find(str) == _obtained.end()) {
//没有重复 //没有重复
_obtained.emplace(str); _obtained.emplace(str);
return str; return str;
} }
} }
} }
void RandStrGeneator::release(const string &str){
void RandStrGeneator::release(const string &str) {
//从防膨胀库中移除 //从防膨胀库中移除
_obtained.erase(str); _obtained.erase(str);
} }
string RandStrGeneator::obtain_l(){ string RandStrGeneator::obtain_l() {
//12个伪随机字节 + 4个递增的整形字节然后md5即为随机字符串 // 12个伪随机字节 + 4个递增的整形字节然后md5即为随机字符串
auto str = makeRandStr(12,false); auto str = makeRandStr(12, false);
str.append((char *)&_index, sizeof(_index)); str.append((char *)&_index, sizeof(_index));
++_index; ++_index;
return MD5(str).hexdigest(); return MD5(str).hexdigest();
} }
}//namespace mediakit } // namespace mediakit

View File

@ -11,13 +11,13 @@
#ifndef SRC_HTTP_COOKIEMANAGER_H #ifndef SRC_HTTP_COOKIEMANAGER_H
#define SRC_HTTP_COOKIEMANAGER_H #define SRC_HTTP_COOKIEMANAGER_H
#include <memory> #include "Common/Parser.h"
#include <unordered_map> #include "Network/Socket.h"
#include "Util/TimeTicker.h"
#include "Util/mini.h" #include "Util/mini.h"
#include "Util/util.h" #include "Util/util.h"
#include "Util/TimeTicker.h" #include <memory>
#include "Network/Socket.h" #include <unordered_map>
#include "Common/Parser.h"
#define COOKIE_DEFAULT_LIFE (7 * 24 * 60 * 60) #define COOKIE_DEFAULT_LIFE (7 * 24 * 60 * 60)
@ -28,9 +28,9 @@ class HttpCookieManager;
/** /**
* cookie对象cookie的一些相关属性 * cookie对象cookie的一些相关属性
*/ */
class HttpServerCookie : public toolkit::AnyStorage , public toolkit::noncopyable{ class HttpServerCookie : public toolkit::noncopyable {
public: public:
typedef std::shared_ptr<HttpServerCookie> Ptr; using Ptr = std::shared_ptr<HttpServerCookie>;
/** /**
* cookie * cookie
* @param manager cookie管理者对象 * @param manager cookie管理者对象
@ -40,12 +40,10 @@ public:
* @param max_elapsed * @param max_elapsed
*/ */
HttpServerCookie(const std::shared_ptr<HttpCookieManager> &manager, HttpServerCookie(
const std::string &cookie_name, const std::shared_ptr<HttpCookieManager> &manager, const std::string &cookie_name, const std::string &uid,
const std::string &uid, const std::string &cookie, uint64_t max_elapsed);
const std::string &cookie, ~HttpServerCookie();
uint64_t max_elapsed);
~HttpServerCookie() ;
/** /**
* uid * uid
@ -65,13 +63,13 @@ public:
* cookie随机字符串 * cookie随机字符串
* @return cookie随机字符串 * @return cookie随机字符串
*/ */
const std::string& getCookie() const; const std::string &getCookie() const;
/** /**
* cookie名 * cookie名
* @return * @return
*/ */
const std::string& getCookieName() const; const std::string &getCookieName() const;
/** /**
* cookie的过期时间cookie不失效 * cookie的过期时间cookie不失效
@ -85,26 +83,35 @@ public:
bool isExpired(); bool isExpired();
/** /**
* *
* @return
*/ */
std::shared_ptr<std::lock_guard<std::recursive_mutex> > getLock(); void setAttach(std::shared_ptr<void> attach);
/*
*
*/
template <class T>
const T& getAttach() const {
return *static_cast<const T *>(_attach.get());
}
private: private:
std::string cookieExpireTime() const ; std::string cookieExpireTime() const;
private: private:
std::string _uid; std::string _uid;
std::string _cookie_name; std::string _cookie_name;
std::string _cookie_uuid; std::string _cookie_uuid;
uint64_t _max_elapsed; uint64_t _max_elapsed;
toolkit::Ticker _ticker; toolkit::Ticker _ticker;
std::recursive_mutex _mtx; std::shared_ptr<void> _attach;
std::weak_ptr<HttpCookieManager> _manager; std::weak_ptr<HttpCookieManager> _manager;
}; };
/** /**
* cookie随机字符串生成器 * cookie随机字符串生成器
*/ */
class RandStrGeneator{ class RandStrGeneator {
public: public:
RandStrGeneator() = default; RandStrGeneator() = default;
~RandStrGeneator() = default; ~RandStrGeneator() = default;
@ -120,8 +127,10 @@ public:
* @param str * @param str
*/ */
void release(const std::string &str); void release(const std::string &str);
private: private:
std::string obtain_l(); std::string obtain_l();
private: private:
//碰撞库 //碰撞库
std::unordered_set<std::string> _obtained; std::unordered_set<std::string> _obtained;
@ -135,8 +144,8 @@ private:
*/ */
class HttpCookieManager : public std::enable_shared_from_this<HttpCookieManager> { class HttpCookieManager : public std::enable_shared_from_this<HttpCookieManager> {
public: public:
typedef std::shared_ptr<HttpCookieManager> Ptr;
friend class HttpServerCookie; friend class HttpServerCookie;
using Ptr = std::shared_ptr<HttpCookieManager>;
~HttpCookieManager(); ~HttpCookieManager();
/** /**
@ -152,7 +161,10 @@ public:
* @param max_elapsed cookie过期时间 * @param max_elapsed cookie过期时间
* @return 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<void> attach = nullptr,
int max_client = 1);
/** /**
* cookie随机字符串查找cookie对象 * cookie随机字符串查找cookie对象
@ -160,7 +172,7 @@ public:
* @param cookie cookie随机字符串 * @param cookie cookie随机字符串
* @return cookie对象nullptr * @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对象 * http头中获取cookie对象
@ -168,7 +180,7 @@ public:
* @param http_header http头 * @param http_header http头
* @return cookie对象 * @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 * uid获取cookie
@ -176,7 +188,7 @@ public:
* @param uid id * @param uid id
* @return cookie对象 * @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使 * cookie使
@ -184,8 +196,10 @@ public:
* @return * @return
*/ */
bool delCookie(const HttpServerCookie::Ptr &cookie); bool delCookie(const HttpServerCookie::Ptr &cookie);
private: private:
HttpCookieManager(); HttpCookieManager();
void onManager(); void onManager();
/** /**
* cookie对象时触发cookie * cookie对象时触发cookie
@ -193,7 +207,7 @@ private:
* @param uid id * @param uid id
* @param cookie cookie随机字符串 * @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对象时触发 * cookie对象时触发
@ -201,7 +215,7 @@ private:
* @param uid id * @param uid id
* @param cookie cookie随机字符串 * @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 * cookie
@ -210,7 +224,7 @@ private:
* @param max_client * @param max_client
* @return cookie随机字符串 * @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 * cookie
@ -218,16 +232,21 @@ private:
* @param cookie cookie随机字符串 * @param cookie cookie随机字符串
* @return true * @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: private:
std::unordered_map<std::string/*cookie_name*/,std::unordered_map<std::string/*cookie*/,HttpServerCookie::Ptr/*cookie_data*/> >_map_cookie; std::unordered_map<
std::unordered_map<std::string/*cookie_name*/,std::unordered_map<std::string/*uid*/,std::map<uint64_t/*cookie time stamp*/,std::string/*cookie*/> > >_map_uid_to_cookie; std::string /*cookie_name*/, std::unordered_map<std::string /*cookie*/, HttpServerCookie::Ptr /*cookie_data*/>>
_map_cookie;
std::unordered_map<
std::string /*cookie_name*/,
std::unordered_map<std::string /*uid*/, std::map<uint64_t /*cookie time stamp*/, std::string /*cookie*/>>>
_map_uid_to_cookie;
std::recursive_mutex _mtx_cookie; std::recursive_mutex _mtx_cookie;
toolkit::Timer::Ptr _timer; toolkit::Timer::Ptr _timer;
RandStrGeneator _geneator; RandStrGeneator _geneator;
}; };
}//namespace mediakit } // namespace mediakit
#endif // SRC_HTTP_COOKIEMANAGER_H
#endif //SRC_HTTP_COOKIEMANAGER_H

View File

@ -33,9 +33,6 @@ static const string kCookieName = "ZL_COOKIE";
static const string kHlsSuffix = "/hls.m3u8"; static const string kHlsSuffix = "/hls.m3u8";
class HttpCookieAttachment { class HttpCookieAttachment {
public:
HttpCookieAttachment() {};
~HttpCookieAttachment() {};
public: public:
//cookie生效作用域本cookie只对该目录下的文件生效 //cookie生效作用域本cookie只对该目录下的文件生效
string _path; string _path;
@ -192,13 +189,13 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st
} }
//拦截hls的播放请求 //拦截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事件 //访问的hls.m3u8结尾我们转换成kBroadcastMediaPlayed事件
Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) { Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) {
//cookie有效期为kHlsCookieSecond //cookie有效期为kHlsCookieSecond
invoker(err, "", kHlsCookieSecond); invoker(err, "", kHlsCookieSecond);
}; };
bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, mediaInfo, auth_invoker, static_cast<SockInfo &>(sender)); bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, media_info, auth_invoker, static_cast<SockInfo &>(sender));
if (!flag) { if (!flag) {
//未开启鉴权,那么允许播放 //未开启鉴权,那么允许播放
auth_invoker(""); auth_invoker("");
@ -247,7 +244,7 @@ public:
* 4cookie中记录的url参数是否跟本次url参数一致 * 4cookie中记录的url参数是否跟本次url参数一致
* 5kBroadcastHttpAccess事件 * 5kBroadcastHttpAccess事件
*/ */
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<void(const string &errMsg, const HttpServerCookie::Ptr &cookie)> &callback) { const function<void(const string &errMsg, const HttpServerCookie::Ptr &cookie)> &callback) {
//获取用户唯一id //获取用户唯一id
auto uid = parser.Params(); auto uid = parser.Params();
@ -265,13 +262,12 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
if (cookie) { if (cookie) {
//找到了cookie对cookie上锁先 //找到了cookie对cookie上锁先
auto lck = cookie->getLock(); auto& attach = cookie->getAttach<HttpCookieAttachment>();
auto attachment = (*cookie)[kCookieName].get<HttpCookieAttachment>(); if (path.find(attach._path) == 0) {
if (path.find(attachment._path) == 0) {
//上次cookie是限定本目录 //上次cookie是限定本目录
if (attachment._err_msg.empty()) { if (attach._err_msg.empty()) {
//上次鉴权成功 //上次鉴权成功
if (attachment._is_hls) { if (attach._is_hls) {
//如果播放的是hls那么刷新hls的cookie(获取ts文件也会刷新) //如果播放的是hls那么刷新hls的cookie(获取ts文件也会刷新)
cookie->updateTime(); cookie->updateTime();
cookie_from_header = false; cookie_from_header = false;
@ -282,7 +278,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
//上次鉴权失败但是如果url参数发生变更那么也重新鉴权下 //上次鉴权失败但是如果url参数发生变更那么也重新鉴权下
if (parser.Params().empty() || parser.Params() == cookie->getUid()) { if (parser.Params().empty() || parser.Params() == cookie->getUid()) {
//url参数未变或者本来就没有url参数那么判断本次请求为重复请求无访问权限 //url参数未变或者本来就没有url参数那么判断本次请求为重复请求无访问权限
callback(attachment._err_msg, cookie_from_header ? nullptr : cookie); callback(attach._err_msg, cookie_from_header ? nullptr : cookie);
return; return;
} }
} }
@ -290,7 +286,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
HttpCookieManager::Instance().delCookie(cookie); 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<SockInfoImp>(); SockInfoImp::Ptr info = std::make_shared<SockInfoImp>();
info->_identifier = sender.getIdentifier(); 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(); info->_local_port = sender.get_local_port();
//该用户从来未获取过cookie这个时候我们广播是否允许该用户访问该http目录 //该用户从来未获取过cookie这个时候我们广播是否允许该用户访问该http目录
HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo, info] HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, media_info, info]
(const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { (const string &err_msg, const string &cookie_path_in, int life_second) {
HttpServerCookie::Ptr cookie; HttpServerCookie::Ptr cookie;
if (cookieLifeSecond) { if (life_second) {
//本次鉴权设置了有效期我们把鉴权结果缓存在cookie中 //本次鉴权设置了有效期我们把鉴权结果缓存在cookie中
string cookie_path = cookie_path_in; string cookie_path = cookie_path_in;
if (cookie_path.empty()) { 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_path = is_dir ? path : path.substr(0, path.rfind("/") + 1);
} }
cookie = HttpCookieManager::Instance().addCookie(kCookieName, uid, cookieLifeSecond); auto attach = std::make_shared<HttpCookieAttachment>();
//对cookie上锁
auto lck = cookie->getLock();
HttpCookieAttachment attachment;
//记录用户能访问的路径 //记录用户能访问的路径
attachment._path = cookie_path; attach->_path = cookie_path;
//记录能否访问 //记录能否访问
attachment._err_msg = errMsg; attach->_err_msg = err_msg;
//记录访问的是否为hls //记录访问的是否为hls
attachment._is_hls = is_hls; attach->_is_hls = is_hls;
if (is_hls) { if (is_hls) {
//hls相关信息 // hls相关信息
attachment._hls_data = std::make_shared<HlsCookieData>(mediaInfo, info); attach->_hls_data = std::make_shared<HlsCookieData>(media_info, info);
//hls未查找MediaSource // hls未查找MediaSource
attachment._have_find_media_source = false; attach->_have_find_media_source = false;
} }
(*cookie)[kCookieName].set<HttpCookieAttachment>(std::move(attachment)); callback(err_msg, HttpCookieManager::Instance().addCookie(kCookieName, uid, life_second, attach));
callback(errMsg, cookie);
} else { } else {
callback(errMsg, nullptr); callback(err_msg, nullptr);
} }
}; };
if (is_hls) { if (is_hls) {
//是hls的播放鉴权,拦截之 //是hls的播放鉴权,拦截之
emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender); emitHlsPlayed(parser, media_info, accessPathInvoker, sender);
return; return;
} }
@ -370,13 +362,13 @@ static string pathCat(const string &a, const string &b){
* 访 * 访
* @param sender * @param sender
* @param parser http请求 * @param parser http请求
* @param mediaInfo http url信息 * @param media_info http url信息
* @param strFile * @param file_path
* @param cb * @param cb
*/ */
static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, const string &strFile, const HttpFileManager::invoker &cb) { 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(strFile, kHlsSuffix); bool is_hls = end_with(file_path, kHlsSuffix);
bool file_exist = File::is_file(strFile.data()); bool file_exist = File::is_file(file_path.data());
if (!is_hls && !file_exist) { if (!is_hls && !file_exist) {
//文件不存在且不是hls,那么直接返回404 //文件不存在且不是hls,那么直接返回404
sendNotFound(cb); sendNotFound(cb);
@ -385,13 +377,13 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
if (is_hls) { if (is_hls) {
//hls那么移除掉后缀获取真实的stream_id并且修改协议为HLS //hls那么移除掉后缀获取真实的stream_id并且修改协议为HLS
const_cast<string &>(mediaInfo._schema) = HLS_SCHEMA; const_cast<string &>(media_info._schema) = HLS_SCHEMA;
replace(const_cast<string &>(mediaInfo._streamid), kHlsSuffix, ""); replace(const_cast<string &>(media_info._streamid), kHlsSuffix, "");
} }
weak_ptr<TcpSession> weakSession = sender.shared_from_this(); weak_ptr<TcpSession> 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(); auto strongSession = weakSession.lock();
if (!strongSession) { if (!strongSession) {
//http客户端已经断开不需要回复 //http客户端已经断开不需要回复
@ -401,80 +393,82 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
//文件鉴权失败 //文件鉴权失败
StrCaseMap headerOut; StrCaseMap headerOut;
if (cookie) { if (cookie) {
auto lck = cookie->getLock(); headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path);
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get<HttpCookieAttachment>()._path);
} }
cb(401, "text/html", headerOut, std::make_shared<HttpStringBody>(errMsg)); cb(401, "text/html", headerOut, std::make_shared<HttpStringBody>(errMsg));
return; 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; StrCaseMap httpHeader;
if (cookie) { if (cookie) {
auto lck = cookie->getLock(); httpHeader["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path);
httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get<HttpCookieAttachment>()._path);
} }
HttpSession::HttpResponseInvoker invoker = [&](int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { HttpSession::HttpResponseInvoker invoker = [&](int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) {
if (cookie && file_exist) { if (cookie && file_exist) {
auto lck = cookie->getLock(); auto& attach = cookie->getAttach<HttpCookieAttachment>();
auto is_hls = (*cookie)[kCookieName].get<HttpCookieAttachment>()._is_hls; if (attach._is_hls) {
if (is_hls) { attach._hls_data->addByteUsage(body->remainSize());
(*cookie)[kCookieName].get<HttpCookieAttachment>()._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) { if (!is_hls) {
//不是hls,直接回复文件或404 //不是hls,直接回复文件或404
response_file(cookie, cb, strFile, parser); response_file(cookie, cb, file_path, parser);
return; return;
} }
//是hls直播判断HLS直播流是否已经注册 //是hls直播判断HLS直播流是否已经注册
bool have_find_media_src = false; bool have_find_media_src = false;
if (cookie) { if (cookie) {
auto lck = cookie->getLock(); auto& attach = cookie->getAttach<HttpCookieAttachment>();
have_find_media_src = (*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source; have_find_media_src = attach._have_find_media_source;
if (!have_find_media_src) { if (!have_find_media_src) {
(*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source = true; const_cast<HttpCookieAttachment &>(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) { if (have_find_media_src) {
//之前该cookie已经通过MediaSource::findAsync查找过了所以现在只以文件系统查找结果为准 //之前该cookie已经通过MediaSource::findAsync查找过了所以现在只以文件系统查找结果为准
response_file(cookie, cb, strFile, parser); response_file(cookie, cb, file_path, parser);
return; return;
} }
//hls文件不存在我们等待其生成并延后回复 //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) { if (cookie) {
auto lck = cookie->getLock();
//尝试添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成) //尝试添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成)
(*cookie)[kCookieName].get<HttpCookieAttachment>()._hls_data->addByteUsage(0); auto &attach = cookie->getAttach<HttpCookieAttachment>();
} attach._hls_data->addByteUsage(0);
if (src && File::is_file(strFile.data())) { attach._hls_data->setMediaSource(dynamic_pointer_cast<HlsMediaSource>(src));
//流和m3u8文件都存在那么直接返回文件
response_file(cookie, cb, strFile, parser);
return;
} }
auto hls = dynamic_pointer_cast<HlsMediaSource>(src); auto hls = dynamic_pointer_cast<HlsMediaSource>(src);
if (!hls) { if (!hls) {
//流不存在,那么直接返回文件(相当于纯粹的HLS文件服务器,但是会挂起播放器15秒左右(用于等待HLS流的注册)) //流不存在,那么直接返回文件(相当于纯粹的HLS文件服务器,但是会挂起播放器15秒左右(用于等待HLS流的注册))
response_file(cookie, cb, strFile, parser); response_file(cookie, cb, file_path, parser);
return; return;
} }
//流存在但是m3u8文件不存在那么等待生成m3u8文件(HLS源注册后并不会立即生成HLS文件有人观看才会按需生成HLS文件) //可能异步获取m3u8索引文件
hls->waitForFile([response_file, cookie, cb, strFile, parser]() { hls->getIndexFile([response_file, file_path, cookie, cb, parser](const string &file) {
response_file(cookie, cb, strFile, parser); 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(bool, enableVhost, General::kEnableVhost);
GET_CONFIG(string, rootPath, Http::kRootPath); GET_CONFIG(string, rootPath, Http::kRootPath);
GET_CONFIG_FUNC(StrCaseMap, virtualPathMap, Http::kVirtualPath, [](const string &str) { 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; string url, path;
auto it = virtualPathMap.find(mediaInfo._app); auto it = virtualPathMap.find(media_info._app);
if (it != virtualPathMap.end()) { if (it != virtualPathMap.end()) {
//访问的是virtualPath //访问的是virtualPath
path = it->second; path = it->second;
url = parser.Url().substr(1 + mediaInfo._app.size()); url = parser.Url().substr(1 + media_info._app.size());
} else { } else {
//访问的是rootPath //访问的是rootPath
path = rootPath; path = rootPath;
url = parser.Url(); 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<SockInfo &>(sender)); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, ret, static_cast<SockInfo &>(sender));
return ret; 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) { void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const HttpFileManager::invoker &cb) {
auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl(); auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl();
MediaInfo mediaInfo(fullUrl); MediaInfo media_info(fullUrl);
auto strFile = getFilePath(parser, mediaInfo, sender); auto file_path = getFilePath(parser, media_info, sender);
//访问的是文件夹 //访问的是文件夹
if (File::is_dir(strFile.data())) { if (File::is_dir(file_path.data())) {
auto indexFile = searchIndexFile(strFile); auto indexFile = searchIndexFile(file_path);
if (!indexFile.empty()) { if (!indexFile.empty()) {
//发现该文件夹下有index文件 //发现该文件夹下有index文件
strFile = pathCat(strFile, indexFile); file_path = pathCat(file_path, indexFile);
parser.setUrl(pathCat(parser.Url(), indexFile)); parser.setUrl(pathCat(parser.Url(), indexFile));
accessFile(sender, parser, mediaInfo, strFile, cb); accessFile(sender, parser, media_info, file_path, cb);
return; return;
} }
string strMenu; string strMenu;
//生成文件夹菜单索引 //生成文件夹菜单索引
if (!makeFolderMenu(parser.Url(), strFile, strMenu)) { if (!makeFolderMenu(parser.Url(), file_path, strMenu)) {
//文件夹不存在 //文件夹不存在
sendNotFound(cb); sendNotFound(cb);
return; 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()) { if (!errMsg.empty()) {
strMenu = errMsg; strMenu = errMsg;
} }
StrCaseMap headerOut; StrCaseMap headerOut;
if (cookie) { if (cookie) {
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get<HttpCookieAttachment>()._path); headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path);
} }
cb(errMsg.empty() ? 200 : 401, "text/html", headerOut, std::make_shared<HttpStringBody>(strMenu)); cb(errMsg.empty() ? 200 : 401, "text/html", headerOut, std::make_shared<HttpStringBody>(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, void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
const StrCaseMap &responseHeader, const StrCaseMap &responseHeader,
const string &filePath, const string &file,
bool use_mmap) const { bool use_mmap,
StrCaseMap &httpHeader = const_cast<StrCaseMap &>(responseHeader); bool is_path) const {
std::shared_ptr<FILE> fp(fopen(filePath.data(), "rb"), [](FILE *fp) { if (!is_path) {
if (fp) { //file是文件内容
fclose(fp); (*this)(200, responseHeader, std::make_shared<HttpStringBody>(file));
} return;
}); }
if (!fp) { //file是文件路径
StrCaseMap &httpHeader = const_cast<StrCaseMap &>(responseHeader);
auto fileBody = std::make_shared<HttpFileBody>(file, use_mmap);
if (fileBody->remainSize() < 0) {
//打开文件失败 //打开文件失败
GET_CONFIG(string, notFound, Http::kNotFound); GET_CONFIG(string, notFound, Http::kNotFound);
GET_CONFIG(string, charSet, Http::kCharSet); GET_CONFIG(string, charSet, Http::kCharSet);
@ -600,29 +597,23 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
} }
auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"]; auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"];
size_t iRangeStart = 0; int code = 200;
size_t iRangeEnd = 0; if (!strRange.empty()) {
size_t fileSize = File::fileSize(fp.get());
int code;
if (strRange.size() == 0) {
//全部下载
code = 200;
iRangeEnd = fileSize - 1;
} else {
//分节下载 //分节下载
code = 206; code = 206;
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data()); auto iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
iRangeEnd = atoll(FindField(strRange.data(), "-", nullptr).data()); auto iRangeEnd = atoll(FindField(strRange.data(), "-", nullptr).data());
auto fileSize = fileBody->remainSize();
if (iRangeEnd == 0) { if (iRangeEnd == 0) {
iRangeEnd = fileSize - 1; iRangeEnd = fileSize - 1;
} }
//设置文件范围
fileBody->setRange(iRangeStart, iRangeEnd - iRangeStart + 1);
//分节下载返回Content-Range头 //分节下载返回Content-Range头
httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl); httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl);
} }
//回复文件 //回复文件
HttpBody::Ptr fileBody = std::make_shared<HttpFileBody>(fp, iRangeStart, iRangeEnd - iRangeStart + 1, use_mmap);
(*this)(code, httpHeader, fileBody); (*this)(code, httpHeader, fileBody);
} }

View File

@ -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 HttpBody::Ptr &body) const;
void operator()(int code, const StrCaseMap &headerOut, const std::string &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(); operator bool();
private: private:
HttpResponseInvokerLambda0 _lambad; HttpResponseInvokerLambda0 _lambad;

View File

@ -519,16 +519,16 @@ void HttpSession::sendResponse(int code,
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
//body默认为空 //body默认为空
ssize_t size = 0; int64_t size = 0;
if (body && body->remainSize()) { if (body && body->remainSize()) {
//有body获取body大小 //有body获取body大小
size = body->remainSize(); size = body->remainSize();
} }
if(no_content_length){ if (no_content_length) {
//http-flv直播是Keep-Alive类型 // http-flv直播是Keep-Alive类型
bClose = false; bClose = false;
}else if((size_t) size >= SIZE_MAX || size < 0 ){ } else if ((size_t)size >= SIZE_MAX || size < 0) {
//不固定长度的body那么发送完body后应该关闭socket以便浏览器做下载完毕的判断 //不固定长度的body那么发送完body后应该关闭socket以便浏览器做下载完毕的判断
bClose = true; bClose = true;
} }
@ -537,47 +537,47 @@ void HttpSession::sendResponse(int code,
headerOut.emplace(kDate, dateStr()); headerOut.emplace(kDate, dateStr());
headerOut.emplace(kServer, kServerName); headerOut.emplace(kServer, kServerName);
headerOut.emplace(kConnection, bClose ? "close" : "keep-alive"); headerOut.emplace(kConnection, bClose ? "close" : "keep-alive");
if(!bClose){ if (!bClose) {
string keepAliveString = "timeout="; string keepAliveString = "timeout=";
keepAliveString += to_string(keepAliveSec); keepAliveString += to_string(keepAliveSec);
keepAliveString += ", max=100"; 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"); 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 //文件长度为固定值,且不是http-flv强制设置Content-Length
headerOut[kContentLength] = to_string(size); headerOut[kContentLength] = to_string(size);
} }
if(size && !pcContentType){ if (size && !pcContentType) {
//有body时设置缺省类型 //有body时设置缺省类型
pcContentType = "text/plain"; pcContentType = "text/plain";
} }
if((size || no_content_length) && pcContentType){ if ((size || no_content_length) && pcContentType) {
//有body时设置文件类型 //有body时设置文件类型
string strContentType = pcContentType; string strContentType = pcContentType;
strContentType += "; charset="; strContentType += "; charset=";
strContentType += charSet; strContentType += charSet;
headerOut.emplace(kContentType,std::move(strContentType)); headerOut.emplace(kContentType, std::move(strContentType));
} }
//发送http头 //发送http头
string str; string str;
str.reserve(256); str.reserve(256);
str += "HTTP/1.1 " ; str += "HTTP/1.1 ";
str += to_string(code); str += to_string(code);
str += ' '; str += ' ';
str += getHttpStatusMessage(code) ; str += getHttpStatusMessage(code);
str += "\r\n"; str += "\r\n";
for (auto &pr : header) { for (auto &pr : header) {
str += pr.first ; str += pr.first;
str += ": "; str += ": ";
str += pr.second; str += pr.second;
str += "\r\n"; str += "\r\n";
@ -586,18 +586,21 @@ void HttpSession::sendResponse(int code,
SockSender::send(std::move(str)); SockSender::send(std::move(str));
_ticker.resetTime(); _ticker.resetTime();
if(!size){ if (!size) {
//没有body //没有body
if(bClose){ if (bClose) {
shutdown(SockException(Err_shutdown,StrPrinter << "close connection after send http header completed with status code:" << code)); shutdown(SockException(Err_shutdown,StrPrinter << "close connection after send http header completed with status code:" << code));
} }
return; return;
} }
#if 0
//sendfile跟共享mmap相比并没有性能上的优势相反sendfile还有功能上的缺陷先屏蔽
if (typeid(*this) == typeid(HttpSession) && !body->sendFile(getSock()->rawFD())) { if (typeid(*this) == typeid(HttpSession) && !body->sendFile(getSock()->rawFD())) {
//http支持sendfile优化 // http支持sendfile优化
return; return;
} }
#endif
GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize);
if (body->remainSize() > sendBufSize) { if (body->remainSize() > sendBufSize) {
@ -607,9 +610,7 @@ void HttpSession::sendResponse(int code,
//发送http body //发送http body
AsyncSenderData::Ptr data = std::make_shared<AsyncSenderData>(shared_from_this(), body, bClose); AsyncSenderData::Ptr data = std::make_shared<AsyncSenderData>(shared_from_this(), body, bClose);
getSock()->setOnFlush([data]() { getSock()->setOnFlush([data]() { return AsyncSender::onSocketFlushed(data); });
return AsyncSender::onSocketFlushed(data);
});
AsyncSender::onSocketFlushed(data); AsyncSender::onSocketFlushed(data);
} }

View File

@ -20,6 +20,7 @@ TsPlayer::TsPlayer(const EventPoller::Ptr &poller) : HttpTSPlayer(poller, true)
void TsPlayer::play(const string &url) { void TsPlayer::play(const string &url) {
TraceL << "play http-ts: " << url; TraceL << "play http-ts: " << url;
_play_result = false; _play_result = false;
_benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
setHeaderTimeout((*this)[Client::kTimeoutMS].as<int>()); setHeaderTimeout((*this)[Client::kTimeoutMS].as<int>());
setBodyTimeout((*this)[Client::kMediaTimeoutMS].as<int>()); setBodyTimeout((*this)[Client::kMediaTimeoutMS].as<int>());
setMethod("GET"); setMethod("GET");
@ -45,7 +46,9 @@ void TsPlayer::onResponseBody(const char *buf, size_t size) {
_play_result = true; _play_result = true;
onPlayResult(SockException(Err_success, "play http-ts success")); onPlayResult(SockException(Err_success, "play http-ts success"));
} }
HttpTSPlayer::onResponseBody(buf, size); if (!_benchmark_mode) {
HttpTSPlayer::onResponseBody(buf, size);
}
} }
} // namespace mediakit } // namespace mediakit

View File

@ -16,7 +16,7 @@
namespace mediakit { namespace mediakit {
class TsPlayer : public HttpTSPlayer , public PlayerBase { class TsPlayer : public HttpTSPlayer, public PlayerBase {
public: public:
TsPlayer(const toolkit::EventPoller::Ptr &poller); TsPlayer(const toolkit::EventPoller::Ptr &poller);
~TsPlayer() override = default; ~TsPlayer() override = default;
@ -37,6 +37,7 @@ protected:
private: private:
bool _play_result = true; bool _play_result = true;
bool _benchmark_mode = false;
}; };
} // namespace mediakit } // namespace mediakit

View File

@ -33,7 +33,8 @@ void TsPlayerImp::addTrackCompleted() {
} }
void TsPlayerImp::onPlayResult(const SockException &ex) { void TsPlayerImp::onPlayResult(const SockException &ex) {
if (ex) { auto benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
if (ex || benchmark_mode) {
PlayerImp<TsPlayer, PlayerBase>::onPlayResult(ex); PlayerImp<TsPlayer, PlayerBase>::onPlayResult(ex);
} else { } else {
auto demuxer = std::make_shared<HlsDemuxer>(); auto demuxer = std::make_shared<HlsDemuxer>();
@ -47,7 +48,10 @@ void TsPlayerImp::onShutdown(const SockException &ex) {
_demuxer = nullptr; _demuxer = nullptr;
} }
vector <Track::Ptr> TsPlayerImp::getTracks(bool ready) const { vector<Track::Ptr> TsPlayerImp::getTracks(bool ready) const {
if (!_demuxer) {
return vector<Track::Ptr>();
}
return static_pointer_cast<HlsDemuxer>(_demuxer)->getTracks(ready); return static_pointer_cast<HlsDemuxer>(_demuxer)->getTracks(ready);
} }

View File

@ -70,7 +70,7 @@ void HlsMaker::makeIndexFile(bool eof) {
snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n"); snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n");
m3u8.append(file_content); m3u8.append(file_content);
} }
onWriteHls(m3u8.data(), m3u8.size()); onWriteHls(m3u8);
} }

View File

@ -72,10 +72,8 @@ protected:
/** /**
* m3u8文件回调 * m3u8文件回调
* @param data
* @param len
*/ */
virtual void onWriteHls(const char *data, size_t len) = 0; virtual void onWriteHls(const std::string &data) = 0;
/** /**
* ts , * ts ,

View File

@ -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); auto hls = makeFile(_path_hls);
if (hls) { if (hls) {
fwrite(data, len, 1, hls.get()); fwrite(data.data(), data.size(), 1, hls.get());
hls.reset(); hls.reset();
if (_media_src) { if (_media_src) {
_media_src->registHls(true); _media_src->registHls(data);
} }
} else { } else {
WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg(); WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg();

View File

@ -53,7 +53,7 @@ protected:
std::string onOpenSegment(uint64_t index) override ; std::string onOpenSegment(uint64_t index) override ;
void onDelSegment(uint64_t index) override; void onDelSegment(uint64_t index) override;
void onWriteSegment(const char *data, size_t len) 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; void onFlushLastSegment(uint32_t duration_ms) override;
private: private:

View File

@ -12,7 +12,7 @@
using namespace toolkit; using namespace toolkit;
namespace mediakit{ namespace mediakit {
HlsCookieData::HlsCookieData(const MediaInfo &info, const std::shared_ptr<SockInfo> &sock_info) { HlsCookieData::HlsCookieData(const MediaInfo &info, const std::shared_ptr<SockInfo> &sock_info) {
_info = info; _info = info;
@ -21,15 +21,15 @@ HlsCookieData::HlsCookieData(const MediaInfo &info, const std::shared_ptr<SockIn
addReaderCount(); addReaderCount();
} }
void HlsCookieData::addReaderCount(){ void HlsCookieData::addReaderCount() {
if(!*_added){ if (!*_added) {
auto src = std::dynamic_pointer_cast<HlsMediaSource>(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); auto src = std::dynamic_pointer_cast<HlsMediaSource>(MediaSource::find(HLS_SCHEMA, _info._vhost, _info._app, _info._streamid));
if(src){ if (src) {
*_added = true; *_added = true;
_ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller()); _ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller());
auto added = _added; auto added = _added;
_ring_reader->setDetachCB([added](){ _ring_reader->setDetachCB([added]() {
//HlsMediaSource已经销毁 // HlsMediaSource已经销毁
*added = false; *added = false;
}); });
} }
@ -39,14 +39,14 @@ void HlsCookieData::addReaderCount(){
HlsCookieData::~HlsCookieData() { HlsCookieData::~HlsCookieData() {
if (*_added) { if (*_added) {
uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000;
WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port() << ") " WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port()
<< "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ") " << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid
<< ")断开,耗时(s):" << duration; << ")断开,耗时(s):" << duration;
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
uint64_t bytes = _bytes.load(); uint64_t bytes = _bytes.load();
if (bytes >= iFlowThreshold * 1024) { if (bytes >= iFlowThreshold * 1024) {
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast<SockInfo&>(*_sock_info)); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast<SockInfo &>(*_sock_info));
} }
} }
} }
@ -57,6 +57,12 @@ void HlsCookieData::addByteUsage(size_t bytes) {
_ticker.resetTime(); _ticker.resetTime();
} }
void HlsCookieData::setMediaSource(const HlsMediaSource::Ptr &src) {
_src = src;
}
}//namespace mediakit HlsMediaSource::Ptr HlsCookieData::getMediaSource() const {
return _src.lock();
}
} // namespace mediakit

View File

@ -11,11 +11,11 @@
#ifndef ZLMEDIAKIT_HLSMEDIASOURCE_H #ifndef ZLMEDIAKIT_HLSMEDIASOURCE_H
#define ZLMEDIAKIT_HLSMEDIASOURCE_H #define ZLMEDIAKIT_HLSMEDIASOURCE_H
#include <atomic>
#include "Util/TimeTicker.h"
#include "Common/MediaSource.h" #include "Common/MediaSource.h"
#include "Util/TimeTicker.h"
#include <atomic>
namespace mediakit{ namespace mediakit {
class HlsMediaSource : public MediaSource { class HlsMediaSource : public MediaSource {
public: public:
@ -24,28 +24,25 @@ public:
using RingType = toolkit::RingBuffer<std::string>; using RingType = toolkit::RingBuffer<std::string>;
using Ptr = std::shared_ptr<HlsMediaSource>; using Ptr = std::shared_ptr<HlsMediaSource>;
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; ~HlsMediaSource() override = default;
/** /**
* *
*/ */
const RingType::Ptr &getRing() const { const RingType::Ptr &getRing() const { return _ring; }
return _ring;
}
/** /**
* *
*/ */
int readerCount() override { int readerCount() override { return _ring ? _ring->readerCount() : 0; }
return _ring ? _ring->readerCount() : 0;
}
/** /**
* m3u8文件时触发 * m3u8文件时触发
* @param file_created hls文件 * @param index_file m3u8文件内容
*/ */
void registHls(bool file_created){ void registHls(std::string index_file) {
if (!_is_regist) { if (!_is_regist) {
_is_regist = true; _is_regist = true;
std::weak_ptr<HlsMediaSource> weakSelf = std::dynamic_pointer_cast<HlsMediaSource>(shared_from_this()); std::weak_ptr<HlsMediaSource> weakSelf = std::dynamic_pointer_cast<HlsMediaSource>(shared_from_this());
@ -61,56 +58,68 @@ public:
regist(); regist();
} }
if (!file_created) { if (index_file.empty()) {
//没产生文件 //没产生索引文件, 只是为了触发媒体注册
return; return;
} }
//m3u8文件生成发送给播放器
decltype(_list_cb) copy; //赋值m3u8索引文件内容
{ std::lock_guard<std::mutex> lck(_mtx_index);
std::lock_guard<std::mutex> lck(_mtx_cb); _index_file = std::move(index_file);
copy.swap(_list_cb);
} _list_cb.for_each([&](const std::function<void(const std::string &str)> &cb) { cb(_index_file); });
copy.for_each([](const std::function<void()> &cb) { _list_cb.clear();
cb();
});
} }
void waitForFile(std::function<void()> cb) { void getIndexFile(std::function<void(const std::string &str)> cb) {
std::lock_guard<std::mutex> lck(_mtx_index);
if (!_index_file.empty()) {
cb(_index_file);
return;
}
//等待生成m3u8文件 //等待生成m3u8文件
std::lock_guard<std::mutex> lck(_mtx_cb);
_list_cb.emplace_back(std::move(cb)); _list_cb.emplace_back(std::move(cb));
} }
void onSegmentSize(size_t bytes) { std::string getIndexFile() const {
_speed[TrackVideo] += bytes; std::lock_guard<std::mutex> lck(_mtx_index);
return _index_file;
} }
void onSegmentSize(size_t bytes) { _speed[TrackVideo] += bytes; }
private: private:
bool _is_regist = false; bool _is_regist = false;
RingType::Ptr _ring; RingType::Ptr _ring;
std::mutex _mtx_cb;
toolkit::List<std::function<void()> > _list_cb; std::string _index_file;
mutable std::mutex _mtx_index;
toolkit::List<std::function<void(const std::string &)> > _list_cb;
}; };
class HlsCookieData{ class HlsCookieData {
public: public:
typedef std::shared_ptr<HlsCookieData> Ptr; using Ptr = std::shared_ptr<HlsCookieData>;
HlsCookieData(const MediaInfo &info, const std::shared_ptr<toolkit::SockInfo> &sock_info); HlsCookieData(const MediaInfo &info, const std::shared_ptr<toolkit::SockInfo> &sock_info);
~HlsCookieData(); ~HlsCookieData();
void addByteUsage(size_t bytes); void addByteUsage(size_t bytes);
void setMediaSource(const HlsMediaSource::Ptr &src);
HlsMediaSource::Ptr getMediaSource() const;
private: private:
void addReaderCount(); void addReaderCount();
private: private:
std::atomic<uint64_t> _bytes {0}; std::atomic<uint64_t> _bytes { 0 };
MediaInfo _info; MediaInfo _info;
std::shared_ptr<bool> _added; std::shared_ptr<bool> _added;
toolkit::Ticker _ticker; toolkit::Ticker _ticker;
std::weak_ptr<HlsMediaSource> _src;
std::shared_ptr<toolkit::SockInfo> _sock_info; std::shared_ptr<toolkit::SockInfo> _sock_info;
HlsMediaSource::RingType::RingReader::Ptr _ring_reader; HlsMediaSource::RingType::RingReader::Ptr _ring_reader;
}; };
}//namespace mediakit } // namespace mediakit
#endif //ZLMEDIAKIT_HLSMEDIASOURCE_H #endif // ZLMEDIAKIT_HLSMEDIASOURCE_H

View File

@ -39,7 +39,7 @@ public:
setDelegate(listener); setDelegate(listener);
_hls->getMediaSource()->setListener(shared_from_this()); _hls->getMediaSource()->setListener(shared_from_this());
//先注册媒体流,后续可以按需生成 //先注册媒体流,后续可以按需生成
_hls->getMediaSource()->registHls(false); _hls->getMediaSource()->registHls("");
} }
int readerCount() { int readerCount() {