hls m3u8文件直接通过内存读写

This commit is contained in:
ziyue 2022-02-11 16:21:19 +08:00
parent 642c9c075a
commit 49fc17d7e0
9 changed files with 96 additions and 69 deletions

View File

@ -399,7 +399,8 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
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 &strFile, const Parser &parser, bool is_path = true) {
StrCaseMap httpHeader; StrCaseMap httpHeader;
if (cookie) { if (cookie) {
httpHeader["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path); httpHeader["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path);
@ -413,7 +414,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
} }
cb(code, HttpFileManager::getContentType(strFile.data()), headerOut, body); cb(code, HttpFileManager::getContentType(strFile.data()), headerOut, body);
}; };
invoker.responseFile(parser.getHeader(), httpHeader, strFile, !is_hls); invoker.responseFile(parser.getHeader(), httpHeader, strFile, !is_hls, is_path);
}; };
if (!is_hls) { if (!is_hls) {
@ -429,6 +430,13 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
have_find_media_src = attach._have_find_media_source; have_find_media_src = attach._have_find_media_source;
if (!have_find_media_src) { if (!have_find_media_src) {
const_cast<HttpCookieAttachment &>(attach)._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, src->getIndexFile(), parser, false);
return;
}
} }
} }
if (have_find_media_src) { if (have_find_media_src) {
@ -440,13 +448,11 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) {
if (cookie) { if (cookie) {
//尝试添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成) //尝试添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成)
cookie->getAttach<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流的注册))
@ -454,9 +460,9 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
return; return;
} }
//流存在但是m3u8文件不存在那么等待生成m3u8文件(HLS源注册后并不会立即生成HLS文件有人观看才会按需生成HLS文件) //可能异步获取m3u8索引文件
hls->waitForFile([response_file, cookie, cb, strFile, parser]() { hls->getIndexFile([response_file, cookie, cb, parser](const string &file) {
response_file(cookie, cb, strFile, parser); response_file(cookie, cb, file, parser, false);
}); });
}); });
}); });
@ -567,10 +573,18 @@ 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,
bool is_path) const {
if (!is_path) {
//file是文件内容
(*this)(200, responseHeader, std::make_shared<HttpStringBody>(file));
return;
}
//file是文件路径
StrCaseMap &httpHeader = const_cast<StrCaseMap &>(responseHeader); StrCaseMap &httpHeader = const_cast<StrCaseMap &>(responseHeader);
auto fileBody = std::make_shared<HttpFileBody>(filePath, use_mmap); auto fileBody = std::make_shared<HttpFileBody>(file, use_mmap);
if (fileBody->remainSize() < 0) { if (fileBody->remainSize() < 0) {
//打开文件失败 //打开文件失败
GET_CONFIG(string, notFound, Http::kNotFound); GET_CONFIG(string, notFound, Http::kNotFound);

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

@ -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() {