Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
胡斌
/
srs
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
winlin
2015-02-24 17:29:30 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
a3648063d5d5ec0f4be5689b2a3e2bfb4e8158f6
a3648063
1 parent
691f7322
fix #179, support dvr http api. 2.0.123.
隐藏空白字符变更
内嵌
并排对比
正在显示
5 个修改的文件
包含
157 行增加
和
0 行删除
README.md
trunk/conf/full.conf
trunk/src/app/srs_app_dvr.cpp
trunk/src/app/srs_app_dvr.hpp
trunk/src/app/srs_app_http_api.cpp
README.md
查看文件 @
a364806
...
...
@@ -532,6 +532,7 @@ Supported operating systems and hardware:
### SRS 2.0 history
*
v2.0, 2015-02-24, fix
[
#179
](
https://github.com/winlinvip/simple-rtmp-server/issues/179
)
, support dvr http api. 2.0.123.
*
v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122.
*
v2.0, 2015-02-19, refine pithy print to more easyer to use. 2.0.121.
*
v2.0, 2015-02-18, fix
[
#133
](
https://github.com/winlinvip/simple-rtmp-server/issues/133
)
, support push rtsp to srs. 2.0.120.
...
...
trunk/conf/full.conf
查看文件 @
a364806
...
...
@@ -314,6 +314,9 @@ vhost dvr.srs.com {
# vhost, stop all dvr of this vhost.
# response in json, where:
# {code:0}
# method=PUT, use as RPC(remote process call).
# reap_segment, the request params in json, where:
# {action:"reap_segment", vhost:"__defaultVhost", path_tmpl:"./[15].[04].[05].[999].flv"}
# when reap segment, the callback POST request in json:
# {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
# app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
...
...
@@ -322,6 +325,7 @@ vhost dvr.srs.com {
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
# default: session
# TODO: FIXME: update wiki for the api plan.
dvr_plan
session
;
# the dvr output path.
# we supports some variables to generate the filename.
...
...
trunk/src/app/srs_app_dvr.cpp
查看文件 @
a364806
...
...
@@ -48,6 +48,9 @@ using namespace std;
// the sleep interval for http async callback.
#define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000
// the use raction for dvr rpc.
#define SRS_DVR_USER_ACTION_REAP_SEGMENT "reap_segment"
SrsFlvSegment
::
SrsFlvSegment
(
SrsDvrPlan
*
p
)
{
req
=
NULL
;
...
...
@@ -1003,6 +1006,10 @@ int SrsDvrApiPlan::on_video(SrsSharedPtrMessage* __video)
sh_video
=
__video
->
copy
();
}
if
((
ret
=
check_user_actions
(
__video
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
((
ret
=
SrsDvrPlan
::
on_video
(
__video
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
...
...
@@ -1101,6 +1108,29 @@ int SrsDvrApiPlan::stop()
return
ret
;
}
int
SrsDvrApiPlan
::
rpc
(
SrsJsonObject
*
obj
)
{
int
ret
=
ERROR_SUCCESS
;
SrsJsonAny
*
prop
=
NULL
;
if
((
prop
=
obj
->
ensure_property_string
(
"action"
))
==
NULL
)
{
ret
=
ERROR_HTTP_DVR_REQUEST
;
srs_error
(
"dvr: rpc required action request. ret=%d"
,
ret
);
return
ret
;
}
action
=
prop
->
to_str
();
if
(
action
==
SRS_DVR_USER_ACTION_REAP_SEGMENT
)
{
if
((
prop
=
obj
->
ensure_property_string
(
"path_tmpl"
))
!=
NULL
)
{
path_template
=
prop
->
to_str
();
}
}
else
{
ret
=
ERROR_HTTP_DVR_REQUEST
;
}
return
ret
;
}
int
SrsDvrApiPlan
::
on_reap_segment
()
{
int
ret
=
ERROR_SUCCESS
;
...
...
@@ -1116,6 +1146,64 @@ int SrsDvrApiPlan::on_reap_segment()
return
ret
;
}
int
SrsDvrApiPlan
::
check_user_actions
(
SrsSharedPtrMessage
*
msg
)
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
segment
);
if
(
action
==
SRS_DVR_USER_ACTION_REAP_SEGMENT
)
{
// when wait keyframe, ignore if no frame arrived.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/177
if
(
_srs_config
->
get_dvr_wait_keyframe
(
req
->
vhost
))
{
if
(
!
msg
->
is_video
())
{
return
ret
;
}
char
*
payload
=
msg
->
payload
;
int
size
=
msg
->
size
;
bool
is_key_frame
=
SrsFlvCodec
::
video_is_h264
(
payload
,
size
)
&&
SrsFlvCodec
::
video_is_keyframe
(
payload
,
size
)
&&
!
SrsFlvCodec
::
video_is_sequence_header
(
payload
,
size
);
if
(
!
is_key_frame
)
{
return
ret
;
}
}
// reap segment
if
((
ret
=
segment
->
close
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// use new path template if user specified.
if
(
!
path_template
.
empty
()
&&
(
ret
=
set_path_tmpl
(
path_template
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// open new flv file
if
((
ret
=
segment
->
open
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// update sequence header
if
(
metadata
&&
(
ret
=
SrsDvrPlan
::
on_meta_data
(
metadata
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
sh_video
&&
(
ret
=
SrsDvrPlan
::
on_video
(
sh_video
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
sh_audio
&&
(
ret
=
SrsDvrPlan
::
on_audio
(
sh_audio
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
// reset rcp params.
action
=
""
;
path_template
=
""
;
return
ret
;
}
SrsDvrAppendPlan
::
SrsDvrAppendPlan
()
{
last_update_time
=
0
;
...
...
@@ -1506,6 +1594,46 @@ int SrsApiDvrPool::stop(string vhost)
return
ret
;
}
int
SrsApiDvrPool
::
rpc
(
SrsJsonAny
*
json
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
json
->
is_object
())
{
ret
=
ERROR_HTTP_DVR_REQUEST
;
srs_error
(
"dvr: rpc required object request. ret=%d"
,
ret
);
return
ret
;
}
SrsJsonObject
*
obj
=
json
->
to_object
();
SrsJsonAny
*
prop
=
NULL
;
if
((
prop
=
obj
->
ensure_property_string
(
"vhost"
))
==
NULL
)
{
ret
=
ERROR_HTTP_DVR_REQUEST
;
srs_error
(
"dvr: rpc required vhost request. ret=%d"
,
ret
);
return
ret
;
}
std
::
string
vhost
=
prop
->
to_str
();
std
::
vector
<
SrsDvrApiPlan
*>
plans
;
for
(
int
i
=
0
;
i
<
(
int
)
dvrs
.
size
();
i
++
)
{
SrsDvrApiPlan
*
plan
=
dvrs
.
at
(
i
);
if
(
!
vhost
.
empty
()
&&
plan
->
req
->
vhost
!=
vhost
)
{
continue
;
}
plans
.
push_back
(
plan
);
}
for
(
int
i
=
0
;
i
<
(
int
)
plans
.
size
();
i
++
)
{
SrsDvrApiPlan
*
plan
=
plans
.
at
(
i
);
if
((
ret
=
plan
->
rpc
(
obj
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
return
ret
;
}
SrsDvr
::
SrsDvr
(
SrsSource
*
s
)
{
source
=
s
;
...
...
trunk/src/app/srs_app_dvr.hpp
查看文件 @
a364806
...
...
@@ -44,6 +44,7 @@ class SrsFileWriter;
class
SrsFlvEncoder
;
class
SrsDvrPlan
;
class
SrsJsonAny
;
class
SrsJsonObject
;
class
SrsThread
;
#include <srs_app_source.hpp>
...
...
@@ -305,6 +306,10 @@ private:
std
::
string
callback
;
bool
autostart
;
bool
started
;
private
:
// user action, reap_segment.
std
::
string
action
;
std
::
string
path_template
;
public
:
SrsDvrApiPlan
();
virtual
~
SrsDvrApiPlan
();
...
...
@@ -322,8 +327,11 @@ public:
virtual
int
start
();
virtual
int
dumps
(
std
::
stringstream
&
ss
);
virtual
int
stop
();
virtual
int
rpc
(
SrsJsonObject
*
obj
);
protected
:
virtual
int
on_reap_segment
();
private
:
virtual
int
check_user_actions
(
SrsSharedPtrMessage
*
msg
);
};
/**
...
...
@@ -389,6 +397,7 @@ public:
virtual
int
dumps
(
std
::
string
vhost
,
std
::
stringstream
&
ss
);
virtual
int
create
(
SrsJsonAny
*
json
);
virtual
int
stop
(
std
::
string
vhost
);
virtual
int
rpc
(
SrsJsonAny
*
json
);
};
/**
...
...
trunk/src/app/srs_app_http_api.cpp
查看文件 @
a364806
...
...
@@ -519,6 +519,21 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ret
)
<<
__SRS_JOBJECT_END
;
}
else
if
(
r
->
is_http_put
())
{
int
ret
=
ERROR_SUCCESS
;
std
::
string
body
=
r
->
body
();
SrsJsonAny
*
json
=
SrsJsonAny
::
loads
((
char
*
)
body
.
c_str
());
if
(
!
json
)
{
ret
=
ERROR_HTTP_JSON_REQUIRED
;
}
else
{
SrsAutoFree
(
SrsJsonAny
,
json
);
ret
=
pool
->
rpc
(
json
);
}
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ret
)
<<
__SRS_JOBJECT_END
;
}
else
{
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ERROR_HTTP_DVR_REQUEST
)
...
...
请
注册
或
登录
后发表评论