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
c0f48999
Commit
c0f48999
authored
Mar 07, 2021
by
xiongziliang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
录制接口支持指定切片时间大小:#747
parent
daedbed7
隐藏空白字符变更
内嵌
并排
正在显示
13 个修改的文件
包含
54 行增加
和
40 行删除
+54
-40
api/include/mk_recorder.h
+2
-1
api/source/mk_recorder.cpp
+2
-2
postman/ZLMediaKit.postman_collection.json
+7
-0
server/FFmpegSource.cpp
+2
-2
server/WebApi.cpp
+5
-4
src/Common/MediaSource.cpp
+4
-4
src/Common/MediaSource.h
+3
-3
src/Common/MultiMediaSourceMuxer.cpp
+7
-7
src/Common/MultiMediaSourceMuxer.h
+2
-2
src/Record/MP4Recorder.cpp
+8
-6
src/Record/MP4Recorder.h
+4
-2
src/Record/Recorder.cpp
+5
-5
src/Record/Recorder.h
+3
-2
没有找到文件。
api/include/mk_recorder.h
查看文件 @
c0f48999
...
...
@@ -63,9 +63,10 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co
* @param app 应用名
* @param stream 流id
* @param customized_path 录像文件保存自定义目录,默认为空或null则自动生成
* @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置
* @return 1代表成功,0代表失败
*/
API_EXPORT
int
API_CALL
mk_recorder_start
(
int
type
,
const
char
*
vhost
,
const
char
*
app
,
const
char
*
stream
,
const
char
*
customized_path
);
API_EXPORT
int
API_CALL
mk_recorder_start
(
int
type
,
const
char
*
vhost
,
const
char
*
app
,
const
char
*
stream
,
const
char
*
customized_path
,
size_t
max_second
);
/**
* 停止录制
...
...
api/source/mk_recorder.cpp
查看文件 @
c0f48999
...
...
@@ -41,9 +41,9 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co
return
Recorder
::
isRecording
((
Recorder
::
type
)
type
,
vhost
,
app
,
stream
);
}
API_EXPORT
int
API_CALL
mk_recorder_start
(
int
type
,
const
char
*
vhost
,
const
char
*
app
,
const
char
*
stream
,
const
char
*
customized_path
){
API_EXPORT
int
API_CALL
mk_recorder_start
(
int
type
,
const
char
*
vhost
,
const
char
*
app
,
const
char
*
stream
,
const
char
*
customized_path
,
size_t
max_second
){
assert
(
vhost
&&
app
&&
stream
);
return
Recorder
::
startRecord
((
Recorder
::
type
)
type
,
vhost
,
app
,
stream
,
customized_path
?
customized_path
:
""
);
return
Recorder
::
startRecord
((
Recorder
::
type
)
type
,
vhost
,
app
,
stream
,
customized_path
?
customized_path
:
""
,
max_second
);
}
API_EXPORT
int
API_CALL
mk_recorder_stop
(
int
type
,
const
char
*
vhost
,
const
char
*
app
,
const
char
*
stream
){
...
...
postman/ZLMediaKit.postman_collection.json
查看文件 @
c0f48999
...
...
@@ -818,7 +818,14 @@
{
"key"
:
"customized_path"
,
"value"
:
null
,
"disabled"
:
true
,
"description"
:
"录像文件保存自定义根目录,为空则采用配置文件设置"
},
{
"key"
:
"max_second"
,
"value"
:
"1000"
,
"disabled"
:
true
,
"description"
:
"MP4录制的切片时间大小,单位秒"
}
]
}
...
...
server/FFmpegSource.cpp
查看文件 @
c0f48999
...
...
@@ -280,10 +280,10 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) {
setDelegate
(
listener
);
src
->
setListener
(
shared_from_this
());
if
(
_enable_hls
)
{
src
->
setupRecord
(
Recorder
::
type_hls
,
true
,
""
);
src
->
setupRecord
(
Recorder
::
type_hls
,
true
,
""
,
0
);
}
if
(
_enable_mp4
)
{
src
->
setupRecord
(
Recorder
::
type_mp4
,
true
,
""
);
src
->
setupRecord
(
Recorder
::
type_mp4
,
true
,
""
,
0
);
}
}
}
...
...
server/WebApi.cpp
查看文件 @
c0f48999
...
...
@@ -877,10 +877,11 @@ void installWebApi() {
CHECK_SECRET
();
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
auto
result
=
Recorder
::
startRecord
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
],
allArgs
[
"customized_path"
]);
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
],
allArgs
[
"customized_path"
],
allArgs
[
"max_second"
].
as
<
size_t
>
());
val
[
"result"
]
=
result
;
val
[
"code"
]
=
result
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"msg"
]
=
result
?
"success"
:
"start record failed"
;
...
...
src/Common/MediaSource.cpp
查看文件 @
c0f48999
...
...
@@ -171,13 +171,13 @@ void MediaSource::onReaderChanged(int size) {
}
}
bool
MediaSource
::
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
bool
MediaSource
::
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
){
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
WarnL
<<
"未设置MediaSource的事件监听者,setupRecord失败:"
<<
getSchema
()
<<
"/"
<<
getVhost
()
<<
"/"
<<
getApp
()
<<
"/"
<<
getId
();
return
false
;
}
return
listener
->
setupRecord
(
*
this
,
type
,
start
,
custom_path
);
return
listener
->
setupRecord
(
*
this
,
type
,
start
,
custom_path
,
max_second
);
}
bool
MediaSource
::
isRecording
(
Recorder
::
type
type
){
...
...
@@ -626,12 +626,12 @@ void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) {
}
}
bool
MediaSourceEventInterceptor
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
bool
MediaSourceEventInterceptor
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
{
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
return
false
;
}
return
listener
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
return
listener
->
setupRecord
(
sender
,
type
,
start
,
custom_path
,
max_second
);
}
bool
MediaSourceEventInterceptor
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
...
...
src/Common/MediaSource.h
查看文件 @
c0f48999
...
...
@@ -77,7 +77,7 @@ public:
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
// 开启或关闭录制
virtual
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
return
false
;
};
virtual
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
{
return
false
;
};
// 获取录制状态
virtual
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
return
false
;
};
// 获取所有track相关信息
...
...
@@ -109,7 +109,7 @@ public:
int
totalReaderCount
(
MediaSource
&
sender
)
override
;
void
onReaderChanged
(
MediaSource
&
sender
,
int
size
)
override
;
void
onRegist
(
MediaSource
&
sender
,
bool
regist
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
override
;
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
override
;
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
override
;
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
uint16_t
local_port
,
const
SockException
&
ex
)
>
&
cb
)
override
;
...
...
@@ -252,7 +252,7 @@ public:
// 该流观看人数变化
void
onReaderChanged
(
int
size
);
// 开启或关闭录制
bool
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
bool
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
);
// 获取录制状态
bool
isRecording
(
Recorder
::
type
type
);
// 开始发送ps-rtp
...
...
src/Common/MultiMediaSourceMuxer.cpp
查看文件 @
c0f48999
...
...
@@ -107,8 +107,8 @@ int MultiMuxerPrivate::totalReaderCount() const {
(
hls
?
hls
->
readerCount
()
:
0
);
}
static
std
::
shared_ptr
<
MediaSinkInterface
>
makeRecorder
(
const
vector
<
Track
::
Ptr
>
&
tracks
,
Recorder
::
type
type
,
const
string
&
custom_path
,
MediaSource
&
sender
){
auto
recorder
=
Recorder
::
createRecorder
(
type
,
sender
.
getVhost
(),
sender
.
getApp
(),
sender
.
getId
(),
custom_path
);
static
std
::
shared_ptr
<
MediaSinkInterface
>
makeRecorder
(
MediaSource
&
sender
,
const
vector
<
Track
::
Ptr
>
&
tracks
,
Recorder
::
type
type
,
const
string
&
custom_path
,
size_t
max_second
){
auto
recorder
=
Recorder
::
createRecorder
(
type
,
sender
.
getVhost
(),
sender
.
getApp
(),
sender
.
getId
(),
custom_path
,
max_second
);
for
(
auto
&
track
:
tracks
)
{
recorder
->
addTrack
(
track
);
}
...
...
@@ -116,12 +116,12 @@ static std::shared_ptr<MediaSinkInterface> makeRecorder(const vector<Track::Ptr>
}
//此函数可能跨线程调用
bool
MultiMuxerPrivate
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
bool
MultiMuxerPrivate
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
){
switch
(
type
)
{
case
Recorder
:
:
type_hls
:
{
if
(
start
&&
!
_hls
)
{
//开始录制
auto
hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
));
auto
hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
makeRecorder
(
sender
,
getTracks
(
true
),
type
,
custom_path
,
max_second
));
if
(
hls
)
{
//设置HlsMediaSource的事件监听器
hls
->
setListener
(
_listener
);
...
...
@@ -136,7 +136,7 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo
case
Recorder
:
:
type_mp4
:
{
if
(
start
&&
!
_mp4
)
{
//开始录制
_mp4
=
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
);
_mp4
=
makeRecorder
(
sender
,
getTracks
(
true
),
type
,
custom_path
,
max_second
);
}
else
if
(
!
start
&&
_mp4
)
{
//停止录制
_mp4
=
nullptr
;
...
...
@@ -326,8 +326,8 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
return
listener
->
totalReaderCount
(
sender
);
}
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
return
_muxer
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
{
return
_muxer
->
setupRecord
(
sender
,
type
,
start
,
custom_path
,
max_second
);
}
bool
MultiMediaSourceMuxer
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
...
...
src/Common/MultiMediaSourceMuxer.h
查看文件 @
c0f48999
...
...
@@ -44,7 +44,7 @@ private:
int
totalReaderCount
()
const
;
void
setTimeStamp
(
uint32_t
stamp
);
void
setTrackListener
(
Listener
*
listener
);
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
);
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
);
bool
isEnabled
();
void
onTrackReady
(
const
Track
::
Ptr
&
track
)
override
;
...
...
@@ -125,7 +125,7 @@ public:
* @param custom_path 开启录制时,指定自定义路径
* @return 是否设置成功
*/
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
,
size_t
max_second
)
override
;
/**
* 获取录制状态
...
...
src/Record/MP4Recorder.cpp
查看文件 @
c0f48999
...
...
@@ -20,16 +20,19 @@ using namespace toolkit;
namespace
mediakit
{
MP4Recorder
::
MP4Recorder
(
const
string
&
strPath
,
const
string
&
strVhost
,
const
string
&
strApp
,
const
string
&
strStreamId
)
{
MP4Recorder
::
MP4Recorder
(
const
string
&
strPath
,
const
string
&
strVhost
,
const
string
&
strApp
,
const
string
&
strStreamId
,
size_t
max_second
)
{
_strPath
=
strPath
;
/////record 业务逻辑//////
_info
.
app
=
strApp
;
_info
.
stream
=
strStreamId
;
_info
.
vhost
=
strVhost
;
_info
.
folder
=
strPath
;
GET_CONFIG
(
size_t
,
recordSec
,
Record
::
kFileSecond
);
_max_second
=
max_second
?
max_second
:
recordSec
;
}
MP4Recorder
::~
MP4Recorder
()
{
closeFile
();
...
...
@@ -104,8 +107,7 @@ void MP4Recorder::closeFile() {
}
void
MP4Recorder
::
inputFrame
(
const
Frame
::
Ptr
&
frame
)
{
GET_CONFIG
(
uint32_t
,
recordSec
,
Record
::
kFileSecond
);
if
(
!
_muxer
||
((
_createFileTicker
.
elapsedTime
()
>
recordSec
*
1000
)
&&
if
(
!
_muxer
||
((
_createFileTicker
.
elapsedTime
()
>
_max_second
*
1000
)
&&
(
!
_haveVideo
||
(
_haveVideo
&&
frame
->
keyFrame
())))
){
//成立条件
//1、_muxer为空
...
...
src/Record/MP4Recorder.h
查看文件 @
c0f48999
...
...
@@ -33,7 +33,8 @@ public:
MP4Recorder
(
const
string
&
strPath
,
const
string
&
strVhost
,
const
string
&
strApp
,
const
string
&
strStreamId
);
const
string
&
strStreamId
,
size_t
max_second
);
virtual
~
MP4Recorder
();
/**
...
...
@@ -55,12 +56,13 @@ private:
void
closeFile
();
void
asyncClose
();
private
:
bool
_haveVideo
=
false
;
size_t
_max_second
;
string
_strPath
;
string
_strFile
;
string
_strFileTmp
;
Ticker
_createFileTicker
;
RecordInfo
_info
;
bool
_haveVideo
=
false
;
MP4Muxer
::
Ptr
_muxer
;
list
<
Track
::
Ptr
>
_tracks
;
};
...
...
src/Record/Recorder.cpp
查看文件 @
c0f48999
...
...
@@ -55,7 +55,7 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s
}
}
std
::
shared_ptr
<
MediaSinkInterface
>
Recorder
::
createRecorder
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
){
std
::
shared_ptr
<
MediaSinkInterface
>
Recorder
::
createRecorder
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
,
size_t
max_second
){
auto
path
=
Recorder
::
getRecordPath
(
type
,
vhost
,
app
,
stream_id
,
customized_path
);
switch
(
type
)
{
case
Recorder
:
:
type_hls
:
{
...
...
@@ -72,7 +72,7 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const st
case
Recorder
:
:
type_mp4
:
{
#if defined(ENABLE_MP4)
return
std
::
make_shared
<
MP4Recorder
>
(
path
,
vhost
,
app
,
stream_id
);
return
std
::
make_shared
<
MP4Recorder
>
(
path
,
vhost
,
app
,
stream_id
,
max_second
);
#else
throw
std
::
invalid_argument
(
"mp4相关功能未打开,请开启ENABLE_MP4宏后编译再测试"
);
#endif
...
...
@@ -90,13 +90,13 @@ bool Recorder::isRecording(type type, const string &vhost, const string &app, co
return
src
->
isRecording
(
type
);
}
bool
Recorder
::
startRecord
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
){
bool
Recorder
::
startRecord
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
,
size_t
max_second
){
auto
src
=
MediaSource
::
find
(
vhost
,
app
,
stream_id
);
if
(
!
src
)
{
WarnL
<<
"未找到相关的MediaSource,startRecord失败:"
<<
vhost
<<
"/"
<<
app
<<
"/"
<<
stream_id
;
return
false
;
}
return
src
->
setupRecord
(
type
,
true
,
customized_path
);
return
src
->
setupRecord
(
type
,
true
,
customized_path
,
max_second
);
}
bool
Recorder
::
stopRecord
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
){
...
...
@@ -104,7 +104,7 @@ bool Recorder::stopRecord(type type, const string &vhost, const string &app, con
if
(
!
src
){
return
false
;
}
return
src
->
setupRecord
(
type
,
false
,
""
);
return
src
->
setupRecord
(
type
,
false
,
""
,
0
);
}
}
/* namespace mediakit */
src/Record/Recorder.h
查看文件 @
c0f48999
...
...
@@ -57,9 +57,10 @@ public:
* @param app 应用名
* @param stream_id 流id
* @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置
* @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置
* @return 对象指针,可能为nullptr
*/
static
std
::
shared_ptr
<
MediaSinkInterface
>
createRecorder
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
=
""
);
static
std
::
shared_ptr
<
MediaSinkInterface
>
createRecorder
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
=
""
,
size_t
max_second
=
0
);
/**
* 获取录制状态
...
...
@@ -80,7 +81,7 @@ public:
* @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置
* @return 成功与否
*/
static
bool
startRecord
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
);
static
bool
startRecord
(
type
type
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
,
const
string
&
customized_path
,
size_t
max_second
);
/**
* 停止录制
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论