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
fddb6a13
Commit
fddb6a13
authored
Dec 16, 2021
by
monktan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加hls 落盘录制
parent
b3dd4401
隐藏空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
313 行增加
和
24 行删除
+313
-24
conf/config.ini
+2
-0
server/WebHook.cpp
+16
-0
src/Common/MultiMediaSourceMuxer.cpp
+47
-4
src/Common/MultiMediaSourceMuxer.h
+1
-0
src/Common/config.cpp
+1
-0
src/Common/config.h
+4
-0
src/Record/HlsMaker.cpp
+25
-11
src/Record/HlsMaker.h
+10
-1
src/Record/HlsMakerImp.cpp
+66
-5
src/Record/HlsMakerImp.h
+10
-2
src/Record/HlsRecorderDisk.cpp
+25
-0
src/Record/HlsRecorderDisk.h
+77
-0
src/Record/Recorder.cpp
+26
-0
src/Record/Recorder.h
+3
-1
没有找到文件。
conf/config.ini
查看文件 @
fddb6a13
...
@@ -124,6 +124,8 @@ on_publish=https://127.0.0.1/index/hook/on_publish
...
@@ -124,6 +124,8 @@ on_publish=https://127.0.0.1/index/hook/on_publish
on_record_mp4
=
https://127.0.0.1/index/hook/on_record_mp4
on_record_mp4
=
https://127.0.0.1/index/hook/on_record_mp4
# 录制 hls ts 切片完成事件
# 录制 hls ts 切片完成事件
on_record_ts
=
https://127.0.0.1/index/hook/on_record_ts
on_record_ts
=
https://127.0.0.1/index/hook/on_record_ts
#录制hls切片完成事件,http api接口录制类型为2
on_record_hls
=
http://127.0.0.1/index/hook/on_record_hls
#rtsp播放鉴权事件,此事件中比对rtsp的用户名密码
#rtsp播放鉴权事件,此事件中比对rtsp的用户名密码
on_rtsp_auth
=
https://127.0.0.1/index/hook/on_rtsp_auth
on_rtsp_auth
=
https://127.0.0.1/index/hook/on_rtsp_auth
#rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
#rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
...
...
server/WebHook.cpp
查看文件 @
fddb6a13
...
@@ -43,6 +43,7 @@ const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader";
...
@@ -43,6 +43,7 @@ const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader";
const
string
kOnHttpAccess
=
HOOK_FIELD
"on_http_access"
;
const
string
kOnHttpAccess
=
HOOK_FIELD
"on_http_access"
;
const
string
kOnServerStarted
=
HOOK_FIELD
"on_server_started"
;
const
string
kOnServerStarted
=
HOOK_FIELD
"on_server_started"
;
const
string
kOnServerKeepalive
=
HOOK_FIELD
"on_server_keepalive"
;
const
string
kOnServerKeepalive
=
HOOK_FIELD
"on_server_keepalive"
;
const
string
kOnRecordHls
=
HOOK_FIELD
"on_record_hls"
;
const
string
kAdminParams
=
HOOK_FIELD
"admin_params"
;
const
string
kAdminParams
=
HOOK_FIELD
"admin_params"
;
const
string
kAliveInterval
=
HOOK_FIELD
"alive_interval"
;
const
string
kAliveInterval
=
HOOK_FIELD
"alive_interval"
;
...
@@ -64,6 +65,7 @@ onceToken token([](){
...
@@ -64,6 +65,7 @@ onceToken token([](){
mINI
::
Instance
()[
kOnHttpAccess
]
=
""
;
mINI
::
Instance
()[
kOnHttpAccess
]
=
""
;
mINI
::
Instance
()[
kOnServerStarted
]
=
""
;
mINI
::
Instance
()[
kOnServerStarted
]
=
""
;
mINI
::
Instance
()[
kOnServerKeepalive
]
=
""
;
mINI
::
Instance
()[
kOnServerKeepalive
]
=
""
;
mINI
::
Instance
()[
kOnRecordHls
]
=
""
;
mINI
::
Instance
()[
kAdminParams
]
=
"secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc"
;
mINI
::
Instance
()[
kAdminParams
]
=
"secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc"
;
mINI
::
Instance
()[
kAliveInterval
]
=
30.0
;
mINI
::
Instance
()[
kAliveInterval
]
=
30.0
;
},
nullptr
);
},
nullptr
);
...
@@ -401,6 +403,20 @@ void installWebHook(){
...
@@ -401,6 +403,20 @@ void installWebHook(){
});
});
#endif //ENABLE_MP4
#endif //ENABLE_MP4
#ifdef ENABLE_HLS
//录制hls文件落盘成功后广播
NoticeCenter
::
Instance
().
addListener
(
nullptr
,
Broadcast
::
kBroadcastRecordHlsDisk
,[](
BroadcastRecordHlsDiskArgs
){
GET_CONFIG
(
bool
,
hook_enable
,
Hook
::
kEnable
)
GET_CONFIG
(
string
,
hook_record_hls
,
Hook
::
kOnRecordHls
);
if
(
!
hook_enable
||
hook_record_hls
.
empty
()){
return
;
}
//执行hook
do_http_hook
(
hook_record_hls
,
getRecordInfo
(
info
),
nullptr
);
});
#endif
NoticeCenter
::
Instance
().
addListener
(
nullptr
,
Broadcast
::
kBroadcastRecordTs
,
[](
BroadcastRecordTsArgs
)
{
NoticeCenter
::
Instance
().
addListener
(
nullptr
,
Broadcast
::
kBroadcastRecordTs
,
[](
BroadcastRecordTsArgs
)
{
GET_CONFIG
(
string
,
hook_record_ts
,
Hook
::
kOnRecordTs
);
GET_CONFIG
(
string
,
hook_record_ts
,
Hook
::
kOnRecordTs
);
if
(
!
hook_enable
||
hook_record_ts
.
empty
())
{
if
(
!
hook_enable
||
hook_record_ts
.
empty
())
{
...
...
src/Common/MultiMediaSourceMuxer.cpp
查看文件 @
fddb6a13
...
@@ -119,13 +119,16 @@ void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr<Listener> &list
...
@@ -119,13 +119,16 @@ void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr<Listener> &list
int
MultiMediaSourceMuxer
::
totalReaderCount
()
const
{
int
MultiMediaSourceMuxer
::
totalReaderCount
()
const
{
auto
hls
=
_hls
;
auto
hls
=
_hls
;
auto
hls_disk
=
_hls_disk
;
auto
ret
=
(
_rtsp
?
_rtsp
->
readerCount
()
:
0
)
+
auto
ret
=
(
_rtsp
?
_rtsp
->
readerCount
()
:
0
)
+
(
_rtmp
?
_rtmp
->
readerCount
()
:
0
)
+
(
_rtmp
?
_rtmp
->
readerCount
()
:
0
)
+
(
_ts
?
_ts
->
readerCount
()
:
0
)
+
(
_ts
?
_ts
->
readerCount
()
:
0
)
+
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
(
_fmp4
?
_fmp4
->
readerCount
()
:
0
)
+
(
_fmp4
?
_fmp4
->
readerCount
()
:
0
)
+
(
_mp4
?
1
:
0
)
+
#endif
#endif
(
hls
?
hls
->
readerCount
()
:
0
);
(
hls
?
hls
->
readerCount
()
:
0
)
+
(
hls_disk
?
1
:
0
);
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
return
ret
+
(
int
)
_rtp_sender
.
size
();
return
ret
+
(
int
)
_rtp_sender
.
size
();
...
@@ -153,6 +156,7 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
...
@@ -153,6 +156,7 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
//此函数可能跨线程调用
//此函数可能跨线程调用
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
{
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
{
bool
ret
=
false
;
switch
(
type
)
{
switch
(
type
)
{
case
Recorder
:
:
type_hls
:
{
case
Recorder
:
:
type_hls
:
{
if
(
start
&&
!
_hls
)
{
if
(
start
&&
!
_hls
)
{
...
@@ -167,7 +171,8 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
...
@@ -167,7 +171,8 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
//停止录制
//停止录制
_hls
=
nullptr
;
_hls
=
nullptr
;
}
}
return
true
;
ret
=
true
;
goto
ret
;
}
}
case
Recorder
:
:
type_mp4
:
{
case
Recorder
:
:
type_mp4
:
{
if
(
start
&&
!
_mp4
)
{
if
(
start
&&
!
_mp4
)
{
...
@@ -177,10 +182,28 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
...
@@ -177,10 +182,28 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
//停止录制
//停止录制
_mp4
=
nullptr
;
_mp4
=
nullptr
;
}
}
return
true
;
ret
=
true
;
goto
ret
;
}
case
Recorder
:
:
type_hls_disk
:
{
if
(
start
&&
!
_hls_disk
)
{
// 开始录制
_hls_disk
=
makeRecorder
(
sender
,
getTracks
(),
type
,
custom_path
,
max_second
);
}
else
if
(
!
start
&&
_hls_disk
)
{
// 停止录制
_hls_disk
=
nullptr
;
}
ret
=
true
;
goto
ret
;
}
default
:
{
ret
=
false
;
goto
ret
;
}
}
default
:
return
false
;
}
}
ret
:
MediaSourceEventInterceptor
::
onReaderChanged
(
sender
,
totalReaderCount
());
return
ret
;
}
}
//此函数可能跨线程调用
//此函数可能跨线程调用
...
@@ -190,6 +213,8 @@ bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type
...
@@ -190,6 +213,8 @@ bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type
return
!!
_hls
;
return
!!
_hls
;
case
Recorder
:
:
type_mp4
:
case
Recorder
:
:
type_mp4
:
return
!!
_mp4
;
return
!!
_mp4
;
case
Recorder
:
:
type_hls_disk
:
return
!!
_hls_disk
;
default
:
default
:
return
false
;
return
false
;
}
}
...
@@ -272,10 +297,17 @@ bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
...
@@ -272,10 +297,17 @@ bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
if
(
hls
)
{
if
(
hls
)
{
ret
=
hls
->
addTrack
(
track
)
?
true
:
ret
;
ret
=
hls
->
addTrack
(
track
)
?
true
:
ret
;
}
}
auto
mp4
=
_mp4
;
auto
mp4
=
_mp4
;
if
(
mp4
)
{
if
(
mp4
)
{
ret
=
mp4
->
addTrack
(
track
)
?
true
:
ret
;
ret
=
mp4
->
addTrack
(
track
)
?
true
:
ret
;
}
}
auto
hls_disk
=
_hls_disk
;
if
(
hls_disk
)
{
ret
=
hls_disk
->
addTrack
(
track
)
?
true
:
ret
;
}
return
ret
;
return
ret
;
}
}
...
@@ -335,6 +367,11 @@ void MultiMediaSourceMuxer::resetTracks() {
...
@@ -335,6 +367,11 @@ void MultiMediaSourceMuxer::resetTracks() {
if
(
mp4
)
{
if
(
mp4
)
{
mp4
->
resetTracks
();
mp4
->
resetTracks
();
}
}
auto
hls_disk
=
_hls_disk
;
if
(
hls_disk
)
{
hls_disk
->
resetTracks
();
}
}
}
//该类实现frame级别的时间戳覆盖
//该类实现frame级别的时间戳覆盖
...
@@ -414,11 +451,17 @@ bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) {
...
@@ -414,11 +451,17 @@ bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) {
if
(
hls
)
{
if
(
hls
)
{
ret
=
hls
->
inputFrame
(
frame
)
?
true
:
ret
;
ret
=
hls
->
inputFrame
(
frame
)
?
true
:
ret
;
}
}
auto
mp4
=
_mp4
;
auto
mp4
=
_mp4
;
if
(
mp4
)
{
if
(
mp4
)
{
ret
=
mp4
->
inputFrame
(
frame
)
?
true
:
ret
;
ret
=
mp4
->
inputFrame
(
frame
)
?
true
:
ret
;
}
}
auto
hls_disk
=
_hls_disk
;
if
(
hls_disk
)
{
ret
=
hls_disk
->
inputFrame
(
frame
)
?
true
:
ret
;
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
ret
=
_fmp4
->
inputFrame
(
frame
)
?
true
:
ret
;
ret
=
_fmp4
->
inputFrame
(
frame
)
?
true
:
ret
;
...
...
src/Common/MultiMediaSourceMuxer.h
查看文件 @
fddb6a13
...
@@ -159,6 +159,7 @@ private:
...
@@ -159,6 +159,7 @@ private:
TSMediaSourceMuxer
::
Ptr
_ts
;
TSMediaSourceMuxer
::
Ptr
_ts
;
MediaSinkInterface
::
Ptr
_mp4
;
MediaSinkInterface
::
Ptr
_mp4
;
HlsRecorder
::
Ptr
_hls
;
HlsRecorder
::
Ptr
_hls
;
MediaSinkInterface
::
Ptr
_hls_disk
;
//对象个数统计
//对象个数统计
ObjectStatistic
<
MultiMediaSourceMuxer
>
_statistic
;
ObjectStatistic
<
MultiMediaSourceMuxer
>
_statistic
;
...
...
src/Common/config.cpp
查看文件 @
fddb6a13
...
@@ -53,6 +53,7 @@ const string kBroadcastShellLogin = "kBroadcastShellLogin";
...
@@ -53,6 +53,7 @@ const string kBroadcastShellLogin = "kBroadcastShellLogin";
const
string
kBroadcastNotFoundStream
=
"kBroadcastNotFoundStream"
;
const
string
kBroadcastNotFoundStream
=
"kBroadcastNotFoundStream"
;
const
string
kBroadcastStreamNoneReader
=
"kBroadcastStreamNoneReader"
;
const
string
kBroadcastStreamNoneReader
=
"kBroadcastStreamNoneReader"
;
const
string
kBroadcastHttpBeforeAccess
=
"kBroadcastHttpBeforeAccess"
;
const
string
kBroadcastHttpBeforeAccess
=
"kBroadcastHttpBeforeAccess"
;
const
string
kBroadcastRecordHlsDisk
=
"kBroadcastRecordHlsDisk"
;
}
//namespace Broadcast
}
//namespace Broadcast
//通用配置项目
//通用配置项目
...
...
src/Common/config.h
查看文件 @
fddb6a13
...
@@ -45,6 +45,10 @@ extern const string kBroadcastRecordMP4;
...
@@ -45,6 +45,10 @@ extern const string kBroadcastRecordMP4;
extern
const
string
kBroadcastRecordTs
;
extern
const
string
kBroadcastRecordTs
;
#define BroadcastRecordTsArgs const RecordInfo &info
#define BroadcastRecordTsArgs const RecordInfo &info
//录制hls文件成功后广播
extern
const
string
kBroadcastRecordHlsDisk
;
#define BroadcastRecordHlsDiskArgs const RecordInfo &info
//收到http api请求广播
//收到http api请求广播
extern
const
string
kBroadcastHttpRequest
;
extern
const
string
kBroadcastHttpRequest
;
#define BroadcastHttpRequestArgs const Parser &parser,const HttpSession::HttpResponseInvoker &invoker,bool &consumed,SockInfo &sender
#define BroadcastHttpRequestArgs const Parser &parser,const HttpSession::HttpResponseInvoker &invoker,bool &consumed,SockInfo &sender
...
...
src/Record/HlsMaker.cpp
查看文件 @
fddb6a13
...
@@ -9,18 +9,19 @@
...
@@ -9,18 +9,19 @@
*/
*/
#include "HlsMaker.h"
#include "HlsMaker.h"
namespace
mediakit
{
namespace
mediakit
{
HlsMaker
::
HlsMaker
(
float
seg_duration
,
uint32_t
seg_number
)
{
HlsMaker
::
HlsMaker
(
float
seg_duration
,
uint32_t
seg_number
,
Recorder
::
type
type
)
{
//最小允许设置为0,0个切片代表点播
//最小允许设置为0,0个切片代表点播
_seg_number
=
seg_number
;
_seg_number
=
seg_number
;
_seg_duration
=
seg_duration
;
_seg_duration
=
seg_duration
;
_type
=
type
;
}
}
HlsMaker
::~
HlsMaker
()
{
HlsMaker
::~
HlsMaker
()
{
}
}
void
HlsMaker
::
makeIndexFile
(
bool
eof
)
{
void
HlsMaker
::
makeIndexFile
(
bool
eof
)
{
char
file_content
[
1024
];
char
file_content
[
1024
];
int
maxSegmentDuration
=
0
;
int
maxSegmentDuration
=
0
;
...
@@ -32,7 +33,7 @@ void HlsMaker::makeIndexFile(bool eof) {
...
@@ -32,7 +33,7 @@ void HlsMaker::makeIndexFile(bool eof) {
}
}
}
}
auto
sequence
=
_seg_number
?
(
_file_index
>
_seg_number
?
_file_index
-
_seg_number
:
0LL
)
:
0LL
;
auto
sequence
=
_type
==
Recorder
::
type_hls
?
(
_seg_number
?
(
_file_index
>
_seg_number
?
_file_index
-
_seg_number
:
0LL
)
:
0LL
)
:
0LL
;
string
m3u8
;
string
m3u8
;
if
(
_seg_number
==
0
)
{
if
(
_seg_number
==
0
)
{
...
@@ -57,19 +58,32 @@ void HlsMaker::makeIndexFile(bool eof) {
...
@@ -57,19 +58,32 @@ void HlsMaker::makeIndexFile(bool eof) {
}
}
m3u8
.
assign
(
file_content
);
m3u8
.
assign
(
file_content
);
string
rm3u8
=
m3u8
;
string
rcontent
;
for
(
auto
&
tp
:
_seg_dur_list
)
{
if
(
_type
==
Recorder
::
type_hls
)
{;
for
(
auto
&
tp
:
_seg_dur_list
)
{
snprintf
(
file_content
,
sizeof
(
file_content
),
"#EXTINF:%.3f,
\n
%s?
\n
"
,
std
::
get
<
0
>
(
tp
)
/
1000.0
,
std
::
get
<
1
>
(
tp
).
data
());
m3u8
.
append
(
file_content
);
}
}
else
if
(
_type
==
Recorder
::
type_hls_disk
)
{
auto
&
tp
=
_seg_dur_list
.
back
();
snprintf
(
file_content
,
sizeof
(
file_content
),
"#EXTINF:%.3f,
\n
%s
\n
"
,
std
::
get
<
0
>
(
tp
)
/
1000.0
,
std
::
get
<
1
>
(
tp
).
data
());
snprintf
(
file_content
,
sizeof
(
file_content
),
"#EXTINF:%.3f,
\n
%s
\n
"
,
std
::
get
<
0
>
(
tp
)
/
1000.0
,
std
::
get
<
1
>
(
tp
).
data
());
m3u8
.
append
(
file_content
);
rcontent
.
assign
(
file_content
);
}
}
if
(
eof
)
{
if
(
eof
)
{
snprintf
(
file_content
,
sizeof
(
file_content
),
"#EXT-X-ENDLIST
\n
"
);
snprintf
(
file_content
,
sizeof
(
file_content
),
"#EXT-X-ENDLIST
\n
"
);
m3u8
.
append
(
file_content
);
m3u8
.
append
(
file_content
);
rcontent
.
append
(
file_content
);
}
}
onWriteHls
(
m3u8
.
data
(),
m3u8
.
size
());
}
if
(
_type
==
Recorder
::
type_hls
)
{
onWriteHls
(
m3u8
.
data
(),
m3u8
.
size
());
}
else
if
(
_type
==
Recorder
::
type_hls_disk
)
{
onWriteRecordM3u8
(
rm3u8
.
data
(),
rm3u8
.
size
(),
rcontent
.
data
(),
rcontent
.
size
());
}
}
void
HlsMaker
::
inputData
(
void
*
data
,
size_t
len
,
uint32_t
timestamp
,
bool
is_idr_fast_packet
)
{
void
HlsMaker
::
inputData
(
void
*
data
,
size_t
len
,
uint32_t
timestamp
,
bool
is_idr_fast_packet
)
{
if
(
data
&&
len
)
{
if
(
data
&&
len
)
{
...
@@ -89,7 +103,7 @@ void HlsMaker::inputData(void *data, size_t len, uint32_t timestamp, bool is_idr
...
@@ -89,7 +103,7 @@ void HlsMaker::inputData(void *data, size_t len, uint32_t timestamp, bool is_idr
}
}
void
HlsMaker
::
delOldSegment
()
{
void
HlsMaker
::
delOldSegment
()
{
if
(
_seg_number
==
0
)
{
if
(
_seg_number
==
0
||
_type
==
Recorder
::
type_hls_disk
)
{
//如果设置为保留0个切片,则认为是保存为点播
//如果设置为保留0个切片,则认为是保存为点播
return
;
return
;
}
}
...
@@ -112,7 +126,7 @@ void HlsMaker::addNewSegment(uint32_t stamp) {
...
@@ -112,7 +126,7 @@ void HlsMaker::addNewSegment(uint32_t stamp) {
}
}
//关闭并保存上一个切片,如果_seg_number==0,那么是点播。
//关闭并保存上一个切片,如果_seg_number==0,那么是点播。
flushLastSegment
(
false
);
flushLastSegment
(
_seg_number
==
0
||
_type
==
Recorder
::
type_hls_disk
);
//新增切片
//新增切片
_last_file_name
=
onOpenSegment
(
_file_index
++
);
_last_file_name
=
onOpenSegment
(
_file_index
++
);
//记录本次切片的起始时间戳
//记录本次切片的起始时间戳
...
@@ -129,7 +143,7 @@ void HlsMaker::flushLastSegment(bool eof){
...
@@ -129,7 +143,7 @@ void HlsMaker::flushLastSegment(bool eof){
if
(
seg_dur
<=
0
)
{
if
(
seg_dur
<=
0
)
{
seg_dur
=
100
;
seg_dur
=
100
;
}
}
_seg_dur_list
.
push_back
(
std
::
make_tuple
(
seg_dur
,
std
::
move
(
_last_file_name
)
));
_seg_dur_list
.
emplace_back
(
seg_dur
,
std
::
move
(
_last_file_name
));
_last_file_name
.
clear
();
_last_file_name
.
clear
();
delOldSegment
();
delOldSegment
();
makeIndexFile
(
eof
);
makeIndexFile
(
eof
);
...
@@ -137,7 +151,7 @@ void HlsMaker::flushLastSegment(bool eof){
...
@@ -137,7 +151,7 @@ void HlsMaker::flushLastSegment(bool eof){
}
}
bool
HlsMaker
::
isLive
()
{
bool
HlsMaker
::
isLive
()
{
return
_seg_number
!=
0
;
return
_seg_number
!=
0
&&
_type
==
Recorder
::
type_hls
;
}
}
void
HlsMaker
::
clear
()
{
void
HlsMaker
::
clear
()
{
...
...
src/Record/HlsMaker.h
查看文件 @
fddb6a13
...
@@ -18,6 +18,8 @@
...
@@ -18,6 +18,8 @@
#include "Util/File.h"
#include "Util/File.h"
#include "Util/util.h"
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/logger.h"
#include "Recorder.h"
using
namespace
toolkit
;
using
namespace
toolkit
;
namespace
mediakit
{
namespace
mediakit
{
...
@@ -28,7 +30,7 @@ public:
...
@@ -28,7 +30,7 @@ public:
* @param seg_duration 切片文件长度
* @param seg_duration 切片文件长度
* @param seg_number 切片个数
* @param seg_number 切片个数
*/
*/
HlsMaker
(
float
seg_duration
=
5
,
uint32_t
seg_number
=
3
);
HlsMaker
(
float
seg_duration
=
5
,
uint32_t
seg_number
=
3
,
Recorder
::
type
type
=
Recorder
::
type_hls
);
virtual
~
HlsMaker
();
virtual
~
HlsMaker
();
/**
/**
...
@@ -79,6 +81,12 @@ protected:
...
@@ -79,6 +81,12 @@ protected:
virtual
void
onWriteHls
(
const
char
*
data
,
size_t
len
)
=
0
;
virtual
void
onWriteHls
(
const
char
*
data
,
size_t
len
)
=
0
;
/**
/**
* 写m3u8文件回调,hls落盘使用
* @param data
* @param len
*/
virtual
void
onWriteRecordM3u8
(
const
char
*
header
,
size_t
hlen
,
const
char
*
body
,
size_t
blen
)
=
0
;
/**
* 上一个 ts 切片写入完成, 可在这里进行通知处理
* 上一个 ts 切片写入完成, 可在这里进行通知处理
* @param duration_ms 上一个 ts 切片的时长, 单位为毫秒
* @param duration_ms 上一个 ts 切片的时长, 单位为毫秒
*/
*/
...
@@ -116,6 +124,7 @@ private:
...
@@ -116,6 +124,7 @@ private:
uint64_t
_file_index
=
0
;
uint64_t
_file_index
=
0
;
string
_last_file_name
;
string
_last_file_name
;
std
::
deque
<
tuple
<
int
,
string
>
>
_seg_dur_list
;
std
::
deque
<
tuple
<
int
,
string
>
>
_seg_dur_list
;
Recorder
::
type
_type
{
Recorder
::
type_hls
};
};
};
}
//namespace mediakit
}
//namespace mediakit
...
...
src/Record/HlsMakerImp.cpp
查看文件 @
fddb6a13
...
@@ -22,7 +22,8 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
...
@@ -22,7 +22,8 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
const
string
&
params
,
const
string
&
params
,
uint32_t
bufSize
,
uint32_t
bufSize
,
float
seg_duration
,
float
seg_duration
,
uint32_t
seg_number
)
:
HlsMaker
(
seg_duration
,
seg_number
)
{
uint32_t
seg_number
,
Recorder
::
type
type
)
:
HlsMaker
(
seg_duration
,
seg_number
,
type
)
{
_poller
=
EventPollerPool
::
Instance
().
getPoller
();
_poller
=
EventPollerPool
::
Instance
().
getPoller
();
_path_prefix
=
m3u8_file
.
substr
(
0
,
m3u8_file
.
rfind
(
'/'
));
_path_prefix
=
m3u8_file
.
substr
(
0
,
m3u8_file
.
rfind
(
'/'
));
_path_hls
=
m3u8_file
;
_path_hls
=
m3u8_file
;
...
@@ -33,16 +34,37 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
...
@@ -33,16 +34,37 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
});
});
_info
.
folder
=
_path_prefix
;
_info
.
folder
=
_path_prefix
;
_start_time
=
::
time
(
nullptr
);
_type
=
type
;
}
}
HlsMakerImp
::~
HlsMakerImp
()
{
HlsMakerImp
::~
HlsMakerImp
()
{
clearCache
(
false
);
clearCache
(
false
,
false
);
}
}
void
HlsMakerImp
::
clearCache
(
bool
immediately
)
{
void
HlsMakerImp
::
clearCache
(
bool
immediately
,
bool
first
)
{
//录制完了
//录制完了
flushLastSegment
(
true
);
flushLastSegment
(
true
);
if
(
!
isLive
())
{
if
(
!
isLive
())
{
if
(
first
)
return
;
//第一次创建清除cache不需要上报
//hook接口,hls落盘录制,触发hook
auto
info
=
_info
;
if
(
_media_src
)
{
info
.
app
=
_media_src
.
get
()
->
getApp
();
info
.
stream
=
_media_src
.
get
()
->
getId
();
info
.
vhost
=
_media_src
.
get
()
->
getVhost
();
info
.
file_path
=
_path_hls
;
info
.
start_time
=
_start_time
;
info
.
time_len
=
::
time
(
nullptr
)
-
_start_time
;
info
.
folder
=
_info
.
folder
;
info
.
file_name
=
_path_hls
;
info
.
url
=
_path_hls
;
info
.
file_size
=
0
;
}
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastRecordHlsDisk
,
info
);
return
;
return
;
}
}
...
@@ -115,13 +137,13 @@ void HlsMakerImp::onWriteHls(const char *data, size_t len) {
...
@@ -115,13 +137,13 @@ void HlsMakerImp::onWriteHls(const char *data, size_t len) {
if
(
hls
)
{
if
(
hls
)
{
fwrite
(
data
,
len
,
1
,
hls
.
get
());
fwrite
(
data
,
len
,
1
,
hls
.
get
());
hls
.
reset
();
hls
.
reset
();
if
(
_media_src
)
{
// 只有直播才注册
if
(
_media_src
&&
_type
==
Recorder
::
type_hls
)
{
_media_src
->
registHls
(
true
);
_media_src
->
registHls
(
true
);
}
}
}
else
{
}
else
{
WarnL
<<
"create hls file failed,"
<<
_path_hls
<<
" "
<<
get_uv_errmsg
();
WarnL
<<
"create hls file failed,"
<<
_path_hls
<<
" "
<<
get_uv_errmsg
();
}
}
//DebugL << "\r\n" << string(data,len);
}
}
void
HlsMakerImp
::
onFlushLastSegment
(
uint32_t
duration_ms
)
{
void
HlsMakerImp
::
onFlushLastSegment
(
uint32_t
duration_ms
)
{
...
@@ -161,4 +183,42 @@ HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {
...
@@ -161,4 +183,42 @@ HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {
return
_media_src
;
return
_media_src
;
}
}
void
HlsMakerImp
::
onWriteRecordM3u8
(
const
char
*
header
,
size_t
hlen
,
const
char
*
body
,
size_t
blen
)
{
bool
exist
=
true
;
string
mode
=
"rb+"
;
if
(
access
(
_path_hls
.
c_str
(),
0
)
==
-
1
)
{
exist
=
false
;
mode
=
"wb+"
;
}
auto
hls_file
=
makeRecordM3u8
(
_path_hls
,
mode
);
if
(
hls_file
)
{
fwrite
(
header
,
hlen
,
1
,
hls_file
.
get
());
if
(
exist
)
{
fseek
(
hls_file
.
get
(),
-
15L
,
SEEK_END
);
}
fwrite
(
body
,
blen
,
1
,
hls_file
.
get
());
hls_file
.
reset
();
if
(
_media_src
&&
_type
==
Recorder
::
type_hls
){
_media_src
->
registHls
(
true
);
}
}
else
{
WarnL
<<
"create hls_file file failed, "
<<
_path_hls
<<
" "
<<
get_uv_errmsg
();
}
}
std
::
shared_ptr
<
FILE
>
HlsMakerImp
::
makeRecordM3u8
(
const
string
&
file
,
const
string
&
mode
,
bool
setbuf
)
{
auto
file_buf
=
_file_buf
;
auto
ret
=
shared_ptr
<
FILE
>
(
File
::
create_file
(
file
.
data
(),
mode
.
data
()),
[
file_buf
](
FILE
*
fp
)
{
if
(
fp
)
{
fclose
(
fp
);
}
});
if
(
ret
&&
setbuf
)
{
setvbuf
(
ret
.
get
(),
_file_buf
.
get
(),
_IOFBF
,
_buf_size
);
}
return
ret
;
}
}
//
namespace
mediakit
}
//
namespace
mediakit
\ No newline at end of file
src/Record/HlsMakerImp.h
查看文件 @
fddb6a13
...
@@ -27,7 +27,8 @@ public:
...
@@ -27,7 +27,8 @@ public:
const
string
&
params
,
const
string
&
params
,
uint32_t
bufSize
=
64
*
1024
,
uint32_t
bufSize
=
64
*
1024
,
float
seg_duration
=
5
,
float
seg_duration
=
5
,
uint32_t
seg_number
=
3
);
uint32_t
seg_number
=
3
,
Recorder
::
type
type
=
Recorder
::
type_hls
);
~
HlsMakerImp
()
override
;
~
HlsMakerImp
()
override
;
...
@@ -49,17 +50,21 @@ public:
...
@@ -49,17 +50,21 @@ public:
* 清空缓存
* 清空缓存
* @param immediately 时候立即删除
* @param immediately 时候立即删除
*/
*/
void
clearCache
(
bool
immediately
=
true
);
void
clearCache
(
bool
immediately
=
true
,
bool
first
=
false
);
protected
:
protected
:
string
onOpenSegment
(
uint64_t
index
)
override
;
string
onOpenSegment
(
uint64_t
index
)
override
;
void
onDelSegment
(
uint64_t
index
)
override
;
void
onDelSegment
(
uint64_t
index
)
override
;
void
onWriteSegment
(
const
char
*
data
,
size_t
len
)
override
;
void
onWriteSegment
(
const
char
*
data
,
size_t
len
)
override
;
void
onWriteHls
(
const
char
*
data
,
size_t
len
)
override
;
void
onWriteHls
(
const
char
*
data
,
size_t
len
)
override
;
// hls 落盘使用
void
onWriteRecordM3u8
(
const
char
*
header
,
size_t
hlen
,
const
char
*
body
,
size_t
blen
)
override
;
void
onFlushLastSegment
(
uint32_t
duration_ms
)
override
;
void
onFlushLastSegment
(
uint32_t
duration_ms
)
override
;
private
:
private
:
std
::
shared_ptr
<
FILE
>
makeFile
(
const
string
&
file
,
bool
setbuf
=
false
);
std
::
shared_ptr
<
FILE
>
makeFile
(
const
string
&
file
,
bool
setbuf
=
false
);
// hls 落盘使用
std
::
shared_ptr
<
FILE
>
makeRecordM3u8
(
const
string
&
file
,
const
string
&
mode
,
bool
setbuf
=
false
);
private
:
private
:
int
_buf_size
;
int
_buf_size
;
...
@@ -72,6 +77,9 @@ private:
...
@@ -72,6 +77,9 @@ private:
HlsMediaSource
::
Ptr
_media_src
;
HlsMediaSource
::
Ptr
_media_src
;
EventPoller
::
Ptr
_poller
;
EventPoller
::
Ptr
_poller
;
map
<
uint64_t
/*index*/
,
string
/*file_path*/
>
_segment_file_paths
;
map
<
uint64_t
/*index*/
,
string
/*file_path*/
>
_segment_file_paths
;
time_t
_start_time
{
0
};
Recorder
::
type
_type
{
Recorder
::
type_hls
};
};
};
}
//namespace mediakit
}
//namespace mediakit
...
...
src/Record/HlsRecorderDisk.cpp
0 → 100644
查看文件 @
fddb6a13
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "HlsRecorderDisk.h"
namespace
mediakit
{
void
HlsRecorderDisk
::
resetTracks
()
{
TsMuxer
::
resetTracks
();
}
bool
HlsRecorderDisk
::
addTrack
(
const
Track
::
Ptr
&
track
)
{
return
TsMuxer
::
addTrack
(
track
);
}
}
//namespace mediakit
src/Record/HlsRecorderDisk.h
0 → 100644
查看文件 @
fddb6a13
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HLSRECORDER_DISK_H
#define HLSRECORDER_DISK_H
#include "Common/MediaSink.h"
#include "HlsMakerImp.h"
#include "TsMuxer.h"
namespace
mediakit
{
class
HlsRecorderDisk
:
public
TsMuxer
,
public
std
::
enable_shared_from_this
<
HlsRecorderDisk
>
{
public
:
typedef
std
::
shared_ptr
<
HlsRecorderDisk
>
Ptr
;
HlsRecorderDisk
(
const
string
&
m3u8_file
,
const
string
&
params
){
GET_CONFIG
(
uint32_t
,
hlsNum
,
Hls
::
kSegmentNum
);
GET_CONFIG
(
uint32_t
,
hlsBufSize
,
Hls
::
kFileBufSize
);
GET_CONFIG
(
float
,
hlsDuration
,
Hls
::
kSegmentDuration
);
_hls
=
std
::
make_shared
<
HlsMakerImp
>
(
m3u8_file
,
params
,
hlsBufSize
,
hlsDuration
,
hlsNum
,
Recorder
::
type_hls_disk
);
//清空上次的残余文件
_hls
->
clearCache
(
false
,
true
);
}
~
HlsRecorderDisk
()
override
=
default
;
/**
* 重置所有Track
*/
void
resetTracks
()
override
;
/**
* 输入frame
*/
//bool inputFrame(const Frame::Ptr &frame) override;
/**
* 添加ready状态的track
*/
bool
addTrack
(
const
Track
::
Ptr
&
track
)
override
;
void
setMediaSource
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
)
{
_hls
->
setMediaSource
(
vhost
,
app
,
stream_id
);
}
bool
inputFrame
(
const
Frame
::
Ptr
&
frame
)
override
{;
if
(
_clear_cache
)
{
_clear_cache
=
false
;
_hls
->
clearCache
();
}
return
TsMuxer
::
inputFrame
(
frame
);
}
private
:
void
onTs
(
std
::
shared_ptr
<
Buffer
>
buffer
,
uint32_t
timestamp
,
bool
is_idr_fast_packet
)
override
{
if
(
!
buffer
)
{
_hls
->
inputData
(
nullptr
,
0
,
timestamp
,
is_idr_fast_packet
);
}
else
{
_hls
->
inputData
(
buffer
->
data
(),
buffer
->
size
(),
timestamp
,
is_idr_fast_packet
);
}
}
private
:
bool
_clear_cache
=
false
;
std
::
shared_ptr
<
HlsMakerImp
>
_hls
;
};
}
//namespace mediakit
#endif //HLSRECORDER_DISK_H
src/Record/Recorder.cpp
查看文件 @
fddb6a13
...
@@ -13,6 +13,7 @@
...
@@ -13,6 +13,7 @@
#include "Common/MediaSource.h"
#include "Common/MediaSource.h"
#include "MP4Recorder.h"
#include "MP4Recorder.h"
#include "HlsRecorder.h"
#include "HlsRecorder.h"
#include "HlsRecorderDisk.h"
using
namespace
toolkit
;
using
namespace
toolkit
;
...
@@ -50,6 +51,21 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s
...
@@ -50,6 +51,21 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s
}
}
return
File
::
absolutePath
(
mp4FilePath
,
recordPath
);
return
File
::
absolutePath
(
mp4FilePath
,
recordPath
);
}
}
case
Recorder
:
:
type_hls_disk
:
{
GET_CONFIG
(
string
,
hlsPath
,
Record
::
kFilePath
);
string
m3u8FilePath
;
if
(
enableVhost
)
{
m3u8FilePath
=
"hls_record/"
+
vhost
+
"/"
+
app
+
"/"
+
stream_id
+
"/record.m3u8"
;
}
else
{
m3u8FilePath
=
"hls_record/"
+
app
+
"/"
+
stream_id
+
"/record.m3u8"
;
}
//Here we use the customized file path.
if
(
!
customized_path
.
empty
())
{
m3u8FilePath
=
"/"
+
stream_id
+
"/record.m3u8"
;
return
File
::
absolutePath
(
m3u8FilePath
,
customized_path
);
}
return
File
::
absolutePath
(
m3u8FilePath
,
hlsPath
);
}
default
:
default
:
return
""
;
return
""
;
}
}
...
@@ -77,6 +93,16 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const st
...
@@ -77,6 +93,16 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const st
throw
std
::
invalid_argument
(
"mp4相关功能未打开,请开启ENABLE_MP4宏后编译再测试"
);
throw
std
::
invalid_argument
(
"mp4相关功能未打开,请开启ENABLE_MP4宏后编译再测试"
);
#endif
#endif
}
}
case
Recorder
:
:
type_hls_disk
:
{
#if defined(ENABLE_HLS)
GET_CONFIG
(
bool
,
enable_vhost
,
General
::
kEnableVhost
);
auto
ret
=
std
::
make_shared
<
HlsRecorderDisk
>
(
path
,
enable_vhost
?
string
(
VHOST_KEY
)
+
"="
+
vhost
:
""
);
ret
->
setMediaSource
(
vhost
,
app
,
stream_id
);
return
ret
;
#else
throw
std
::
invalid_argument
(
"hls相关功能未打开,请开启ENABLE_HLS宏后编译再测试"
);
#endif
}
default
:
throw
std
::
invalid_argument
(
"未知的录制类型"
);
default
:
throw
std
::
invalid_argument
(
"未知的录制类型"
);
}
}
...
...
src/Record/Recorder.h
查看文件 @
fddb6a13
...
@@ -36,7 +36,9 @@ public:
...
@@ -36,7 +36,9 @@ public:
// 录制hls
// 录制hls
type_hls
=
0
,
type_hls
=
0
,
// 录制MP4
// 录制MP4
type_mp4
=
1
type_mp4
=
1
,
// 录制hls落盘
type_hls_disk
=
2
,
}
type
;
}
type
;
/**
/**
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论