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