mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
HLS: Stat the HLS streaming clients bandwidth. v5.0.49
This commit is contained in:
parent
c1df280211
commit
d7c2d5ab01
9 changed files with 98 additions and 18 deletions
|
@ -41,5 +41,8 @@ vhost __defaultVhost__ {
|
||||||
enabled on;
|
enabled on;
|
||||||
mount [vhost]/[app]/[stream].flv;
|
mount [vhost]/[app]/[stream].flv;
|
||||||
}
|
}
|
||||||
|
hls {
|
||||||
|
enabled on;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
||||||
|
|
||||||
## SRS 5.0 Changelog
|
## SRS 5.0 Changelog
|
||||||
|
|
||||||
|
* v5.0, 2022-08-29, HLS: Stat the HLS streaming clients bandwidth. v5.0.49
|
||||||
* v5.0, 2022-08-28, URL: Use SrsHttpUri to parse URL and query. v5.0.48
|
* v5.0, 2022-08-28, URL: Use SrsHttpUri to parse URL and query. v5.0.48
|
||||||
* v5.0, 2022-08-28, Fix [#2881](https://github.com/ossrs/srs/issues/2881): HTTP: Support merging api to server. v5.0.47
|
* v5.0, 2022-08-28, Fix [#2881](https://github.com/ossrs/srs/issues/2881): HTTP: Support merging api to server. v5.0.47
|
||||||
* v5.0, 2022-08-27, Fix [#3108](https://github.com/ossrs/srs/issues/3108): STAT: Update stat for SRT. v5.0.46
|
* v5.0, 2022-08-27, Fix [#3108](https://github.com/ossrs/srs/issues/3108): STAT: Update stat for SRT. v5.0.46
|
||||||
|
|
|
@ -68,7 +68,7 @@ SrsHlsStream::~SrsHlsStream()
|
||||||
map_ctx_info_.clear();
|
map_ctx_info_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHlsStream::serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, SrsRequest* req)
|
srs_error_t SrsHlsStream::serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, ISrsFileReaderFactory* factory, string fullpath, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -79,18 +79,40 @@ srs_error_t SrsHlsStream::serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
|
|
||||||
// Already exists context, response with rebuilt m3u8 content.
|
// Already exists context, response with rebuilt m3u8 content.
|
||||||
if (!ctx.empty() && ctx_is_exist(ctx)) {
|
if (!ctx.empty() && ctx_is_exist(ctx)) {
|
||||||
return serve_exists_session(w, r, fullpath);
|
return serve_exists_session(w, r, factory, fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a m3u8 in memory, contains the session id(ctx).
|
// Create a m3u8 in memory, contains the session id(ctx).
|
||||||
return serve_new_session(w, r, req);
|
return serve_new_session(w, r, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SrsHlsStream::on_serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||||
|
{
|
||||||
|
string ctx = r->query_get(SRS_CONTEXT_IN_HLS);
|
||||||
|
if (ctx.empty() || !ctx_is_exist(ctx)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
|
srs_assert(hr);
|
||||||
|
|
||||||
|
SrsHttpConn* hc = dynamic_cast<SrsHttpConn*>(hr->connection());
|
||||||
|
srs_assert(hc);
|
||||||
|
|
||||||
|
ISrsKbpsDelta* conn = dynamic_cast<ISrsKbpsDelta*>(hc);
|
||||||
|
srs_assert(conn);
|
||||||
|
|
||||||
|
// Only update the delta, because SrsServer will sample it. Note that SrsServer also does the stat for all clients
|
||||||
|
// including this one, but it should be ignored because the id is not matched, and instead we use the hls_ctx as
|
||||||
|
// session id to match the client.
|
||||||
|
SrsStatistic::instance()->kbps_add_delta(ctx, conn);
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRequest* req)
|
srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
SrsHttpMessage *hr = dynamic_cast<SrsHttpMessage *>(r);
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
srs_assert(hr);
|
srs_assert(hr);
|
||||||
|
|
||||||
string ctx;
|
string ctx;
|
||||||
|
@ -140,18 +162,20 @@ srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter* w, ISrsHttpM
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHlsStream::serve_exists_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath)
|
srs_error_t SrsHlsStream::serve_exists_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, ISrsFileReaderFactory* factory, std::string fullpath)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// Read m3u8 content.
|
// Read m3u8 content.
|
||||||
SrsFileReader fs;
|
SrsFileReader* fs = factory->create_file_reader();
|
||||||
if ((err = fs.open(fullpath)) != srs_success) {
|
SrsAutoFree(SrsFileReader, fs);
|
||||||
|
|
||||||
|
if ((err = fs->open(fullpath)) != srs_success) {
|
||||||
return srs_error_wrap(err, "open %s", fullpath.c_str());
|
return srs_error_wrap(err, "open %s", fullpath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
string content;
|
string content;
|
||||||
if ((err = srs_ioutil_read_all(&fs, content)) != srs_success) {
|
if ((err = srs_ioutil_read_all(fs, content)) != srs_success) {
|
||||||
return srs_error_wrap(err, "read %s", fullpath.c_str());
|
return srs_error_wrap(err, "read %s", fullpath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,14 +469,14 @@ srs_error_t SrsVodStream::serve_m3u8_ctx(ISrsHttpResponseWriter * w, ISrsHttpMes
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
SrsHttpMessage *hr = dynamic_cast<SrsHttpMessage *>(r);
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
srs_assert(hr);
|
srs_assert(hr);
|
||||||
|
|
||||||
SrsRequest *req = hr->to_request(hr->host())->as_http();
|
SrsRequest* req = hr->to_request(hr->host())->as_http();
|
||||||
SrsAutoFree(SrsRequest, req);
|
SrsAutoFree(SrsRequest, req);
|
||||||
|
|
||||||
// discovery vhost, resolve the vhost from config
|
// discovery vhost, resolve the vhost from config
|
||||||
SrsConfDirective *parsed_vhost = _srs_config->get_vhost(req->vhost);
|
SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
|
||||||
if (parsed_vhost) {
|
if (parsed_vhost) {
|
||||||
req->vhost = parsed_vhost->arg0();
|
req->vhost = parsed_vhost->arg0();
|
||||||
}
|
}
|
||||||
|
@ -464,7 +488,23 @@ srs_error_t SrsVodStream::serve_m3u8_ctx(ISrsHttpResponseWriter * w, ISrsHttpMes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to serve by HLS streaming.
|
// Try to serve by HLS streaming.
|
||||||
return hls_.serve_m3u8_ctx(w, r, fullpath, req);
|
return hls_.serve_m3u8_ctx(w, r, fs_factory, fullpath, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsVodStream::serve_ts_ctx(ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// SrsServer also stat all HTTP connections including this one, but it should be ignored because the id is not
|
||||||
|
// matched to any exists client. And we will do stat for the HLS streaming by session in hls_ctx.
|
||||||
|
|
||||||
|
// Serve by default HLS handler.
|
||||||
|
err = SrsHttpFileServer::serve_ts_ctx(w, r, fullpath);
|
||||||
|
|
||||||
|
// Notify the HLS to stat the ts after serving.
|
||||||
|
hls_.on_serve_ts_ctx(w, r);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpStaticServer::SrsHttpStaticServer(SrsServer* svr)
|
SrsHttpStaticServer::SrsHttpStaticServer(SrsServer* svr)
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include <srs_app_http_conn.hpp>
|
#include <srs_app_http_conn.hpp>
|
||||||
|
|
||||||
|
class ISrsFileReaderFactory;
|
||||||
|
|
||||||
struct SrsM3u8CtxInfo
|
struct SrsM3u8CtxInfo
|
||||||
{
|
{
|
||||||
srs_utime_t request_time;
|
srs_utime_t request_time;
|
||||||
|
@ -29,10 +31,11 @@ public:
|
||||||
SrsHlsStream();
|
SrsHlsStream();
|
||||||
virtual ~SrsHlsStream();
|
virtual ~SrsHlsStream();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, SrsRequest* req);
|
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, ISrsFileReaderFactory* factory, std::string fullpath, SrsRequest* req);
|
||||||
|
virtual void on_serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
private:
|
private:
|
||||||
srs_error_t serve_new_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRequest *req);
|
srs_error_t serve_new_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRequest *req);
|
||||||
srs_error_t serve_exists_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
srs_error_t serve_exists_session(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, ISrsFileReaderFactory* factory, std::string fullpath);
|
||||||
bool ctx_is_exist(std::string ctx);
|
bool ctx_is_exist(std::string ctx);
|
||||||
void alive(std::string ctx, SrsRequest* req);
|
void alive(std::string ctx, SrsRequest* req);
|
||||||
srs_error_t http_hooks_on_play(SrsRequest* req);
|
srs_error_t http_hooks_on_play(SrsRequest* req);
|
||||||
|
@ -60,6 +63,7 @@ protected:
|
||||||
virtual srs_error_t serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t start, int64_t end);
|
virtual srs_error_t serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t start, int64_t end);
|
||||||
// Support HLS streaming with pseudo session id.
|
// Support HLS streaming with pseudo session id.
|
||||||
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
|
virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The http static server instance,
|
// The http static server instance,
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 5
|
#define VERSION_MAJOR 5
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 48
|
#define VERSION_REVISION 49
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -382,6 +382,8 @@ srs_error_t SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMes
|
||||||
return serve_mp4_file(w, r, fullpath);
|
return serve_mp4_file(w, r, fullpath);
|
||||||
} else if (srs_string_ends_with(upath, ".m3u8")) {
|
} else if (srs_string_ends_with(upath, ".m3u8")) {
|
||||||
return serve_m3u8_file(w, r, fullpath);
|
return serve_m3u8_file(w, r, fullpath);
|
||||||
|
} else if (srs_string_ends_with(upath, ".ts")) {
|
||||||
|
return serve_ts_file(w, r, fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve common static file.
|
// serve common static file.
|
||||||
|
@ -531,6 +533,11 @@ srs_error_t SrsHttpFileServer::serve_m3u8_file(ISrsHttpResponseWriter * w, ISrsH
|
||||||
return serve_m3u8_ctx(w, r, fullpath);
|
return serve_m3u8_ctx(w, r, fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpFileServer::serve_ts_file(ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
|
||||||
|
{
|
||||||
|
return serve_ts_ctx(w, r, fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int64_t offset)
|
srs_error_t SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int64_t offset)
|
||||||
{
|
{
|
||||||
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
|
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
|
||||||
|
@ -548,7 +555,12 @@ srs_error_t SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsH
|
||||||
srs_error_t SrsHttpFileServer::serve_m3u8_ctx(ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
|
srs_error_t SrsHttpFileServer::serve_m3u8_ctx(ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
|
||||||
{
|
{
|
||||||
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
|
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
|
||||||
// TODO: FIXME: Support range in header https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Range_requests
|
return serve_file(w, r, fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpFileServer::serve_ts_ctx(ISrsHttpResponseWriter * w, ISrsHttpMessage * r, std::string fullpath)
|
||||||
|
{
|
||||||
|
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
|
||||||
return serve_file(w, r, fullpath);
|
return serve_file(w, r, fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -286,6 +286,7 @@ private:
|
||||||
virtual srs_error_t serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
virtual srs_error_t serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
virtual srs_error_t serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
virtual srs_error_t serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
virtual srs_error_t serve_m3u8_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
virtual srs_error_t serve_m3u8_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
|
virtual srs_error_t serve_ts_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
protected:
|
protected:
|
||||||
// When access flv file with x.flv?start=xxx
|
// When access flv file with x.flv?start=xxx
|
||||||
virtual srs_error_t serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t offset);
|
virtual srs_error_t serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t offset);
|
||||||
|
@ -304,6 +305,7 @@ protected:
|
||||||
// Remark 2:
|
// Remark 2:
|
||||||
// If use two same "hls_ctx" in different requests, SRS cannot detect so that they will be treated as one.
|
// If use two same "hls_ctx" in different requests, SRS cannot detect so that they will be treated as one.
|
||||||
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
|
virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
|
||||||
protected:
|
protected:
|
||||||
// Copy the fs to response writer in size bytes.
|
// Copy the fs to response writer in size bytes.
|
||||||
virtual srs_error_t copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int64_t size);
|
virtual srs_error_t copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int64_t size);
|
||||||
|
|
|
@ -1225,7 +1225,7 @@ VOID TEST(ProtocolHTTPTest, VodStreamHandlers)
|
||||||
HELPER_ASSERT_SUCCESS(r.set_url("/index.m3u8", false));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.m3u8", false));
|
||||||
|
|
||||||
HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r));
|
HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r));
|
||||||
__MOCK_HTTP_EXPECT_STRCT(200, "index.m3u8?hls_ctx=", w);
|
__MOCK_HTTP_EXPECT_STRHAS(200, "index.m3u8?hls_ctx=", w);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should return "hls_ctx"
|
// Should return "hls_ctx"
|
||||||
|
@ -1243,7 +1243,25 @@ VOID TEST(ProtocolHTTPTest, VodStreamHandlers)
|
||||||
HELPER_ASSERT_SUCCESS(r.set_url("/index.m3u8?hls_ctx=123456", false));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.m3u8?hls_ctx=123456", false));
|
||||||
|
|
||||||
HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r));
|
HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r));
|
||||||
__MOCK_HTTP_EXPECT_STRCT(200, "index.m3u8?hls_ctx=123456", w);
|
__MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should return "hls_ctx"
|
||||||
|
if (true) {
|
||||||
|
SrsHttpMuxEntry e;
|
||||||
|
e.pattern = "/";
|
||||||
|
|
||||||
|
SrsVodStream h("/tmp");
|
||||||
|
h.set_fs_factory(new MockFileReaderFactory("livestream-13.ts"));
|
||||||
|
h.set_path_check(_mock_srs_path_always_exists);
|
||||||
|
h.entry = &e;
|
||||||
|
|
||||||
|
MockResponseWriter w;
|
||||||
|
SrsHttpMessage r(NULL, NULL);
|
||||||
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.m3u8?hls_ctx=123456", false));
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r));
|
||||||
|
__MOCK_HTTP_EXPECT_STREQ(200, "livestream-13.ts?hls_ctx=123456", w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ bool is_string_contain(string substr, string str);
|
||||||
#define __MOCK_HTTP_EXPECT_STREQ2(status, text, w) \
|
#define __MOCK_HTTP_EXPECT_STREQ2(status, text, w) \
|
||||||
EXPECT_STREQ(mock_http_response2(status, text).c_str(), HELPER_BUFFER2STR(&w.io.out_buffer).c_str())
|
EXPECT_STREQ(mock_http_response2(status, text).c_str(), HELPER_BUFFER2STR(&w.io.out_buffer).c_str())
|
||||||
|
|
||||||
#define __MOCK_HTTP_EXPECT_STRCT(status, text, w) \
|
#define __MOCK_HTTP_EXPECT_STRHAS(status, text, w) \
|
||||||
EXPECT_PRED2(is_string_contain, text, HELPER_BUFFER2STR(&w.io.out_buffer).c_str())
|
EXPECT_PRED2(is_string_contain, text, HELPER_BUFFER2STR(&w.io.out_buffer).c_str())
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue