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-03-01 17:57:28 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
fb3fced8d01809787f2835587d3369cd591c32c6
fb3fced8
1 parent
45059839
for #179, revert dvr http api. 2.0.128.
隐藏空白字符变更
内嵌
并排对比
正在显示
10 个修改的文件
包含
7 行增加
和
932 行删除
README.md
trunk/conf/full.conf
trunk/src/app/srs_app_config.cpp
trunk/src/app/srs_app_config.hpp
trunk/src/app/srs_app_dvr.cpp
trunk/src/app/srs_app_dvr.hpp
trunk/src/app/srs_app_http_api.cpp
trunk/src/app/srs_app_http_api.hpp
trunk/src/app/srs_app_server.cpp
trunk/src/core/srs_core.hpp
README.md
查看文件 @
fb3fced
...
...
@@ -513,8 +513,6 @@ Supported operating systems and hardware:
[
#304
](
https://github.com/winlinvip/simple-rtmp-server/issues/304
)
.
1.
Support push RTSP to SRS, read
[
#133
](
https://github.com/winlinvip/simple-rtmp-server/issues/133
)
.
1.
Support DVR http api, read
[
#179
](
https://github.com/winlinvip/simple-rtmp-server/issues/179
)
.
1.
[
no-plan
]
Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
1.
[
no-plan
]
Support RTMP 302 redirect
[
#92
](
https://github.com/winlinvip/simple-rtmp-server/issues/92
)
.
1.
[
no-plan
]
Support multiple processes, for both origin and edge
...
...
@@ -552,10 +550,9 @@ Supported operating systems and hardware:
## History
### SRS 2.0 history
*
v2.0, 2015-02-24, for
[
#179
](
https://github.com/winlinvip/simple-rtmp-server/issues/179
)
, dvr suport vhost/app/stream level control. 2.0.125.
*
v2.0, 2015-02-24, for
[
#304
](
https://github.com/winlinvip/simple-rtmp-server/issues/304
)
, fix hls bug, write pts/dts error. 2.0.124.
*
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-03-01, for
[
#179
](
https://github.com/winlinvip/simple-rtmp-server/issues/179
)
, revert dvr http api. 2.0.128.
*
v2.0, 2015-02-24, for
[
#304
](
https://github.com/winlinvip/simple-rtmp-server/issues/304
)
, fix hls bug, write pts/dts error. 2.0.124
*
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
查看文件 @
fb3fced
...
...
@@ -288,52 +288,7 @@ vhost dvr.srs.com {
# session reap flv when session end(unpublish).
# segment reap flv when flv duration exceed the specified dvr_duration.
# append always append to flv file, never reap it.
# api reap flv when api required.
# about the api plan, the HTTP api to dvr,
# http url to control dvr, for example, http://dev:1985/api/v1/dvrs
# method=GET
# to query dvrs of server.
# request params, for example ?vhost=__defaultVhost__&&app=live&&stream=livestream, where:
# vhost, <required>, query all dvr of this vhost.
# app, [optinal], query all dvr of this app. query all app if not specified.
# stream, [optional], query specified dvr stream. query all stream if not specified.
# response in json, where:
# {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv",
# vhost:"__defaultVhost", app:"live", stream:"livestream",
# wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs",
# status:"stop"|"start"
# }]}
# method=POST
# to start dvr of specified vhost.
# request should encode in json, specifies the dvr to create, where:
# {path_tmpl:"./[15].[04].[05].[999].flv",
# vhost:"__defaultVhost", app:"live", stream:"livestream",
# wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs"
# }
# @remark, the app and stream is required for POST.
# response in json, where:
# {code:0}
# method=DELETE, to stop dvr
# to stop dvr of specified vhost.
# request params, for example ?vhost=__defaultVhost__, where:
# 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", app:"live", stream:"livestream",
# path_tmpl:"./[15].[04].[05].[999].flv"
# }
# @remark, the app and stream is optional.
# 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"
# }
# for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.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.
...
...
@@ -369,27 +324,20 @@ vhost dvr.srs.com {
# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path
# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path
# segment,session apply it.
# api apply before api specified the path.
# default: ./objs/nginx/html
dvr_path
./
objs
/
nginx
/
html
;
# the duration for dvr file, reap if exeed, in seconds.
# segment apply it.
# session,ap
i
ignore.
# session,ap
pend
ignore.
# default: 30
dvr_duration
30
;
# whether wait keyframe to reap segment,
# if off, reap segment when duration exceed the dvr_duration,
# if on, reap segment when duration exceed and got keyframe.
# segment apply it.
# session,ap
i
ignore.
# session,ap
pend
ignore.
# default: on
dvr_wait_keyframe
on
;
# whether dvr auto start when publish.
# if off, dvr wait for api to start it.
# api apply it.
# segment,session ignore.
# default: on
dvr_autostart
on
;
# about the stream monotonically increasing:
# 1. video timestamp is monotonically increasing,
# 2. audio timestamp is monotonically increasing,
...
...
trunk/src/app/srs_app_config.cpp
查看文件 @
fb3fced
...
...
@@ -1418,7 +1418,6 @@ int SrsConfig::check_config()
string
m
=
conf
->
at
(
j
)
->
name
.
c_str
();
if
(
m
!=
"enabled"
&&
m
!=
"dvr_path"
&&
m
!=
"dvr_plan"
&&
m
!=
"dvr_duration"
&&
m
!=
"dvr_wait_keyframe"
&&
m
!=
"time_jitter"
&&
m
!=
"dvr_autostart"
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"unsupported vhost dvr directive %s, ret=%d"
,
m
.
c_str
(),
ret
);
...
...
@@ -1977,41 +1976,6 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* sc)
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
SrsConfDirective
*
SrsConfig
::
create_directive
(
string
vhost
,
string
directive
,
string
sub_directive
)
{
SrsConfDirective
*
vhost_conf
=
get_vhost
(
vhost
);
if
(
!
vhost_conf
)
{
vhost_conf
=
new
SrsConfDirective
();
vhost_conf
->
name
=
vhost
;
root
->
directives
.
push_back
(
vhost_conf
);
}
if
(
directive
.
empty
())
{
return
vhost_conf
;
}
SrsConfDirective
*
dir
=
vhost_conf
->
get
(
directive
);
if
(
!
dir
)
{
dir
=
new
SrsConfDirective
();
dir
->
name
=
directive
;
vhost_conf
->
directives
.
push_back
(
dir
);
}
if
(
sub_directive
.
empty
())
{
return
dir
;
}
SrsConfDirective
*
sdir
=
dir
->
get
(
sub_directive
);
if
(
!
sdir
)
{
sdir
=
new
SrsConfDirective
();
sdir
->
name
=
sub_directive
;
dir
->
directives
.
push_back
(
sdir
);
}
return
sdir
;
}
SrsConfDirective
*
SrsConfig
::
get_vhost
(
string
vhost
)
{
srs_assert
(
root
);
...
...
@@ -2355,13 +2319,6 @@ bool SrsConfig::get_vhost_http_hooks_enabled(string vhost)
return
true
;
}
void
SrsConfig
::
set_vhost_http_hooks_enabled
(
string
vhost
,
bool
enabled
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"http_hooks"
,
"enabled"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
enabled
?
"on"
:
"off"
);
}
SrsConfDirective
*
SrsConfig
::
get_vhost_on_connect
(
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost_http_hooks
(
vhost
);
...
...
@@ -2439,13 +2396,6 @@ SrsConfDirective* SrsConfig::get_vhost_on_dvr(string vhost)
return
conf
->
get
(
"on_dvr"
);
}
void
SrsConfig
::
set_vhost_on_dvr
(
string
vhost
,
string
callback
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"http_hooks"
,
"on_dvr"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
callback
);
}
bool
SrsConfig
::
get_bw_check_enabled
(
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
...
...
@@ -3359,13 +3309,6 @@ bool SrsConfig::get_dvr_enabled(string vhost)
return
false
;
}
void
SrsConfig
::
set_dvr_enabled
(
string
vhost
,
bool
enabled
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"dvr"
,
"enabled"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
enabled
?
"on"
:
"off"
);
}
string
SrsConfig
::
get_dvr_path
(
string
vhost
)
{
SrsConfDirective
*
dvr
=
get_dvr
(
vhost
);
...
...
@@ -3383,13 +3326,6 @@ string SrsConfig::get_dvr_path(string vhost)
return
conf
->
arg0
();
}
void
SrsConfig
::
set_dvr_path
(
string
vhost
,
string
path
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"dvr"
,
"dvr_path"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
path
);
}
string
SrsConfig
::
get_dvr_plan
(
string
vhost
)
{
SrsConfDirective
*
dvr
=
get_dvr
(
vhost
);
...
...
@@ -3407,13 +3343,6 @@ string SrsConfig::get_dvr_plan(string vhost)
return
conf
->
arg0
();
}
void
SrsConfig
::
set_dvr_plan
(
string
vhost
,
string
plan
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"dvr"
,
"dvr_plan"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
plan
);
}
int
SrsConfig
::
get_dvr_duration
(
string
vhost
)
{
SrsConfDirective
*
dvr
=
get_dvr
(
vhost
);
...
...
@@ -3448,30 +3377,6 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost)
return
false
;
}
void
SrsConfig
::
set_dvr_wait_keyframe
(
string
vhost
,
bool
wait_keyframe
)
{
SrsConfDirective
*
conf
=
create_directive
(
vhost
,
"dvr"
,
"dvr_wait_keyframe"
);
conf
->
args
.
clear
();
conf
->
args
.
push_back
(
wait_keyframe
?
"on"
:
"off"
);
}
bool
SrsConfig
::
get_dvr_autostart
(
string
vhost
)
{
SrsConfDirective
*
dvr
=
get_dvr
(
vhost
);
if
(
!
dvr
)
{
return
true
;
}
SrsConfDirective
*
conf
=
dvr
->
get
(
"dvr_autostart"
);
if
(
!
conf
||
conf
->
arg0
()
!=
"off"
)
{
return
true
;
}
return
false
;
}
int
SrsConfig
::
get_dvr_time_jitter
(
string
vhost
)
{
SrsConfDirective
*
dvr
=
get_dvr
(
vhost
);
...
...
trunk/src/app/srs_app_config.hpp
查看文件 @
fb3fced
...
...
@@ -61,7 +61,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
#define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment"
#define SRS_CONF_DEFAULT_DVR_PLAN_APPEND "append"
#define SRS_CONF_DEFAULT_DVR_PLAN_API "api"
#define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION
#define SRS_CONF_DEFAULT_DVR_DURATION 30
#define SRS_CONF_DEFAULT_TIME_JITTER "full"
...
...
@@ -452,14 +451,6 @@ public:
* get the max udp port for rtp of stream caster rtsp.
*/
virtual
int
get_stream_caster_rtp_port_max
(
SrsConfDirective
*
sc
);
private
:
/**
* create directive under vhost.
* @param directive, get the directive of vhost. get vhost if directive is empty.
* @param sub_directive, get the sub directive of vhost. get directive if sub-directive is empty.
* @return the vhost(empty directive and sub-directive); the directive(empty sub-directive); the sub-directive.
*/
virtual
SrsConfDirective
*
create_directive
(
std
::
string
vhost
,
std
::
string
directive
,
std
::
string
sub_directive
);
// vhost specified section
public:
/**
...
...
@@ -596,7 +587,6 @@ public:
* @remark, if not enabled, donot callback all http hooks.
*/
virtual
bool
get_vhost_http_hooks_enabled
(
std
::
string
vhost
);
virtual
void
set_vhost_http_hooks_enabled
(
std
::
string
vhost
,
bool
enabled
);
/**
* get the on_connect callbacks of vhost.
* @return the on_connect callback directive, the args is the url to callback.
...
...
@@ -632,7 +622,6 @@ public:
* @return the on_dvr callback directive, the args is the url to callback.
*/
virtual
SrsConfDirective
*
get_vhost_on_dvr
(
std
::
string
vhost
);
virtual
void
set_vhost_on_dvr
(
std
::
string
vhost
,
std
::
string
callback
);
// bwct(bandwidth check tool) section
public:
/**
...
...
@@ -925,17 +914,14 @@ public:
* whether dvr is enabled.
*/
virtual
bool
get_dvr_enabled
(
std
::
string
vhost
);
virtual
void
set_dvr_enabled
(
std
::
string
vhost
,
bool
enabled
);
/**
* get the dvr path, the flv file to save in.
*/
virtual
std
::
string
get_dvr_path
(
std
::
string
vhost
);
virtual
void
set_dvr_path
(
std
::
string
vhost
,
std
::
string
path
);
/**
* get the plan of dvr, how to reap the flv file.
*/
virtual
std
::
string
get_dvr_plan
(
std
::
string
vhost
);
virtual
void
set_dvr_plan
(
std
::
string
vhost
,
std
::
string
plan
);
/**
* get the duration of dvr flv.
*/
...
...
@@ -944,11 +930,6 @@ public:
* whether wait keyframe to reap segment.
*/
virtual
bool
get_dvr_wait_keyframe
(
std
::
string
vhost
);
virtual
void
set_dvr_wait_keyframe
(
std
::
string
vhost
,
bool
wait_keyframe
);
/**
* whether autostart for dvr. wait api to start dvr if false.
*/
virtual
bool
get_dvr_autostart
(
std
::
string
vhost
);
/**
* get the time_jitter algorithm for dvr.
*/
...
...
trunk/src/app/srs_app_dvr.cpp
查看文件 @
fb3fced
...
...
@@ -785,17 +785,6 @@ SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
return
new
SrsDvrSessionPlan
();
}
else
if
(
plan
==
SRS_CONF_DEFAULT_DVR_PLAN_APPEND
)
{
return
new
SrsDvrAppendPlan
();
}
else
if
(
plan
==
SRS_CONF_DEFAULT_DVR_PLAN_API
)
{
/**
* @remark the api plan maybe create by publish event or http api post create dvr event.
* so when we got from pool first when create it.
*/
SrsApiDvrPool
*
pool
=
SrsApiDvrPool
::
instance
();
SrsDvrApiPlan
*
plan
=
pool
->
get_dvr
(
vhost
);
if
(
plan
)
{
return
plan
;
}
return
new
SrsDvrApiPlan
();
}
else
{
srs_error
(
"invalid dvr plan=%s, vhost=%s"
,
plan
.
c_str
(),
vhost
.
c_str
());
srs_assert
(
false
);
...
...
@@ -852,318 +841,6 @@ void SrsDvrSessionPlan::on_unpublish()
dvr_enabled
=
false
;
}
SrsDvrApiPlan
::
SrsDvrApiPlan
()
{
autostart
=
false
;
started
=
false
;
metadata
=
sh_audio
=
sh_video
=
NULL
;
}
SrsDvrApiPlan
::~
SrsDvrApiPlan
()
{
SrsApiDvrPool
*
pool
=
SrsApiDvrPool
::
instance
();
pool
->
detach_dvr
(
this
);
srs_freep
(
metadata
);
srs_freep
(
sh_audio
);
srs_freep
(
sh_video
);
}
int
SrsDvrApiPlan
::
initialize
(
SrsRequest
*
r
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsDvrPlan
::
initialize
(
r
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
SrsApiDvrPool
*
pool
=
SrsApiDvrPool
::
instance
();
if
((
ret
=
pool
->
add_dvr
(
this
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
autostart
=
_srs_config
->
get_dvr_autostart
(
r
->
vhost
);
return
ret
;
}
int
SrsDvrApiPlan
::
on_publish
()
{
int
ret
=
ERROR_SUCCESS
;
// support multiple publish.
if
(
dvr_enabled
)
{
return
ret
;
}
if
(
!
_srs_config
->
get_dvr_enabled
(
req
->
vhost
))
{
return
ret
;
}
// api disabled dvr when not autostart.
bool
autostart
=
_srs_config
->
get_dvr_autostart
(
req
->
vhost
);
if
(
!
autostart
&&
!
started
)
{
srs_warn
(
"dvr: api not start and disabled for not autostart."
);
return
ret
;
}
dvr_enabled
=
true
;
if
((
ret
=
segment
->
close
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
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
;
}
return
ret
;
}
void
SrsDvrApiPlan
::
on_unpublish
()
{
}
int
SrsDvrApiPlan
::
on_meta_data
(
SrsSharedPtrMessage
*
__metadata
)
{
int
ret
=
ERROR_SUCCESS
;
srs_freep
(
metadata
);
metadata
=
__metadata
->
copy
();
return
ret
;
}
int
SrsDvrApiPlan
::
on_audio
(
SrsSharedPtrMessage
*
__audio
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
SrsFlvCodec
::
audio_is_sequence_header
(
__audio
->
payload
,
__audio
->
size
))
{
srs_freep
(
sh_audio
);
sh_audio
=
__audio
->
copy
();
}
if
((
ret
=
SrsDvrPlan
::
on_audio
(
__audio
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsDvrApiPlan
::
on_video
(
SrsSharedPtrMessage
*
__video
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
SrsFlvCodec
::
video_is_sequence_header
(
__video
->
payload
,
__video
->
size
))
{
srs_freep
(
sh_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
;
}
return
ret
;
}
int
SrsDvrApiPlan
::
set_plan
()
{
_srs_config
->
set_dvr_plan
(
req
->
vhost
,
SRS_CONF_DEFAULT_DVR_PLAN_API
);
return
ERROR_SUCCESS
;
}
int
SrsDvrApiPlan
::
set_path_tmpl
(
string
path_tmpl
)
{
_srs_config
->
set_dvr_path
(
req
->
vhost
,
path_tmpl
);
return
ERROR_SUCCESS
;
}
int
SrsDvrApiPlan
::
set_callback
(
string
value
)
{
_srs_config
->
set_vhost_http_hooks_enabled
(
req
->
vhost
,
true
);
_srs_config
->
set_vhost_on_dvr
(
req
->
vhost
,
value
);
return
ERROR_SUCCESS
;
}
int
SrsDvrApiPlan
::
set_wait_keyframe
(
bool
wait_keyframe
)
{
_srs_config
->
set_dvr_wait_keyframe
(
req
->
vhost
,
wait_keyframe
);
return
ERROR_SUCCESS
;
}
int
SrsDvrApiPlan
::
start
()
{
int
ret
=
ERROR_SUCCESS
;
if
(
started
)
{
return
ret
;
}
// enable the config.
_srs_config
->
set_dvr_enabled
(
req
->
vhost
,
true
);
// stop dvr
if
(
dvr_enabled
)
{
// ignore error.
int
ret
=
segment
->
close
();
if
(
ret
!=
ERROR_SUCCESS
)
{
srs_warn
(
"ignore flv close error. ret=%d"
,
ret
);
}
dvr_enabled
=
false
;
}
// start dvr
if
((
ret
=
on_publish
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
started
=
true
;
return
ret
;
}
int
SrsDvrApiPlan
::
dumps
(
stringstream
&
ss
)
{
int
ret
=
ERROR_SUCCESS
;
bool
wait_keyframe
=
_srs_config
->
get_dvr_wait_keyframe
(
req
->
vhost
);
std
::
string
path_template
=
_srs_config
->
get_dvr_path
(
req
->
vhost
);
SrsConfDirective
*
callbacks
=
_srs_config
->
get_vhost_on_dvr
(
req
->
vhost
);
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_STR
(
"path_tmpl"
,
path_template
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"path_dvr"
,
segment
->
get_path
())
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_BOOL
(
"wait_keyframe"
,
wait_keyframe
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"vhost"
,
req
->
vhost
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"app"
,
req
->
app
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"stream"
,
req
->
stream
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"callback"
,
callbacks
->
arg0
())
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"status"
,
(
dvr_enabled
?
"start"
:
"stop"
))
<<
__SRS_JOBJECT_END
;
return
ret
;
}
int
SrsDvrApiPlan
::
stop
()
{
int
ret
=
ERROR_SUCCESS
;
_srs_config
->
set_dvr_enabled
(
req
->
vhost
,
false
);
started
=
false
;
// stop dvr
if
(
dvr_enabled
)
{
// ignore error.
int
ret
=
segment
->
close
();
if
(
ret
!=
ERROR_SUCCESS
)
{
srs_warn
(
"ignore flv close error. ret=%d"
,
ret
);
}
dvr_enabled
=
false
;
}
srs_trace
(
"dvr: stop dvr of vhost=%s"
,
req
->
vhost
.
c_str
());
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
::
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
;
...
...
@@ -1420,290 +1097,6 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
return
ret
;
}
SrsApiDvrPool
*
SrsApiDvrPool
::
_instance
=
new
SrsApiDvrPool
();
SrsApiDvrPool
*
SrsApiDvrPool
::
instance
()
{
return
SrsApiDvrPool
::
_instance
;
}
SrsApiDvrPool
::
SrsApiDvrPool
()
{
}
SrsApiDvrPool
::~
SrsApiDvrPool
()
{
dvrs
.
clear
();
}
SrsDvrApiPlan
*
SrsApiDvrPool
::
get_dvr
(
string
vhost
)
{
std
::
vector
<
SrsDvrApiPlan
*>::
iterator
it
;
for
(
it
=
dvrs
.
begin
();
it
!=
dvrs
.
end
();
++
it
)
{
SrsDvrApiPlan
*
plan
=
*
it
;
if
(
plan
->
req
->
vhost
==
vhost
)
{
return
plan
;
}
}
return
NULL
;
}
int
SrsApiDvrPool
::
add_dvr
(
SrsDvrApiPlan
*
dvr
)
{
dvrs
.
push_back
(
dvr
);
return
ERROR_SUCCESS
;
}
void
SrsApiDvrPool
::
detach_dvr
(
SrsDvrApiPlan
*
dvr
)
{
std
::
vector
<
SrsDvrApiPlan
*>::
iterator
it
;
it
=
::
find
(
dvrs
.
begin
(),
dvrs
.
end
(),
dvr
);
if
(
it
!=
dvrs
.
end
())
{
dvrs
.
erase
(
it
);
}
}
int
SrsApiDvrPool
::
dumps
(
string
vhost
,
string
app
,
string
stream
,
stringstream
&
ss
)
{
int
ret
=
ERROR_SUCCESS
;
ss
<<
__SRS_JARRAY_START
;
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
;
}
if
(
!
app
.
empty
()
&&
plan
->
req
->
app
!=
app
)
{
continue
;
}
if
(
!
stream
.
empty
()
&&
plan
->
req
->
stream
!=
stream
)
{
continue
;
}
plans
.
push_back
(
plan
);
}
for
(
int
i
=
0
;
i
<
(
int
)
plans
.
size
();
i
++
)
{
SrsDvrApiPlan
*
plan
=
plans
.
at
(
i
);
if
((
ret
=
plan
->
dumps
(
ss
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
i
<
(
int
)
plans
.
size
()
-
1
)
{
ss
<<
__SRS_JFIELD_CONT
;
}
}
ss
<<
__SRS_JARRAY_END
;
return
ret
;
}
int
SrsApiDvrPool
::
create
(
SrsJsonAny
*
json
)
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
json
);
if
(
!
json
->
is_object
())
{
ret
=
ERROR_HTTP_DVR_CREATE_REQUEST
;
srs_error
(
"dvr: api create dvr request requires json object. 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_CREATE_REQUEST
;
srs_error
(
"dvr: api create dvr request requires vhost. ret=%d"
,
ret
);
return
ret
;
}
std
::
string
vhost
=
prop
->
to_str
();
if
((
prop
=
obj
->
ensure_property_string
(
"app"
))
==
NULL
)
{
ret
=
ERROR_HTTP_DVR_CREATE_REQUEST
;
srs_error
(
"dvr: api create dvr request requires app. ret=%d"
,
ret
);
return
ret
;
}
std
::
string
app
=
prop
->
to_str
();
if
((
prop
=
obj
->
ensure_property_string
(
"stream"
))
==
NULL
)
{
ret
=
ERROR_HTTP_DVR_CREATE_REQUEST
;
srs_error
(
"dvr: api create dvr request requires stream. ret=%d"
,
ret
);
return
ret
;
}
std
::
string
stream
=
prop
->
to_str
();
if
(
vhost
.
empty
()
||
app
.
empty
()
||
stream
.
empty
())
{
ret
=
ERROR_HTTP_DVR_CREATE_REQUEST
;
srs_error
(
"dvr: api create dvr request requires vhost/app/stream. ret=%d"
,
ret
);
return
ret
;
}
SrsDvrApiPlan
*
dvr
=
NULL
;
for
(
int
i
=
0
;
i
<
(
int
)
dvrs
.
size
();
i
++
)
{
SrsDvrApiPlan
*
plan
=
dvrs
.
at
(
i
);
if
(
plan
->
req
->
vhost
!=
vhost
||
plan
->
req
->
app
!=
app
||
plan
->
req
->
stream
!=
stream
)
{
continue
;
}
dvr
=
plan
;
break
;
}
// mock the client request for dvr.
SrsRequest
*
req
=
new
SrsRequest
();
SrsAutoFree
(
SrsRequest
,
req
);
// should notice the source to reload dvr when already publishing.
SrsSource
*
source
=
NULL
;
// create if not exists
if
(
!
dvr
)
{
dvr
=
new
SrsDvrApiPlan
();
req
->
vhost
=
vhost
;
req
->
app
=
app
;
req
->
stream
=
stream
;
req
->
tcUrl
=
"rtmp://"
+
vhost
+
"/"
+
app
+
"/"
+
stream
;
// fetch source from pool.
// NULL, create without source, ignore.
// start dvr when already publishing.
source
=
SrsSource
::
fetch
(
req
);
// initialize for dvr pool to create it.
if
((
ret
=
dvr
->
initialize
(
req
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
// update optional parameters for plan.
if
((
ret
=
dvr
->
set_plan
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
((
prop
=
obj
->
ensure_property_string
(
"path_tmpl"
))
!=
NULL
)
{
if
((
ret
=
dvr
->
set_path_tmpl
(
prop
->
to_str
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
if
((
prop
=
obj
->
ensure_property_boolean
(
"wait_keyframe"
))
!=
NULL
)
{
if
((
ret
=
dvr
->
set_wait_keyframe
(
prop
->
to_boolean
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
if
((
prop
=
obj
->
ensure_property_string
(
"callback"
))
!=
NULL
)
{
if
((
ret
=
dvr
->
set_callback
(
prop
->
to_str
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
if
((
ret
=
dvr
->
start
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// do reload for source when already publishing.
// when reload, the source will use the request instead.
if
(
source
)
{
if
((
ret
=
source
->
on_reload_vhost_dvr
(
vhost
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
return
ret
;
}
int
SrsApiDvrPool
::
stop
(
string
vhost
,
string
app
,
string
stream
)
{
int
ret
=
ERROR_SUCCESS
;
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
;
}
if
(
!
app
.
empty
()
&&
plan
->
req
->
app
!=
app
)
{
continue
;
}
if
(
!
stream
.
empty
()
&&
plan
->
req
->
stream
!=
stream
)
{
continue
;
}
plans
.
push_back
(
plan
);
}
if
(
plans
.
empty
())
{
ret
=
ERROR_HTTP_DVR_NO_TAEGET
;
srs_error
(
"dvr: stop not found for url=%s/%s/%s, ret=%d"
,
vhost
.
c_str
(),
app
.
c_str
(),
stream
.
c_str
(),
ret
);
return
ret
;
}
for
(
int
i
=
0
;
i
<
(
int
)
plans
.
size
();
i
++
)
{
SrsDvrApiPlan
*
plan
=
plans
.
at
(
i
);
if
((
ret
=
plan
->
stop
())
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
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
::
string
app
,
stream
;
if
((
prop
=
obj
->
ensure_property_string
(
"app"
))
!=
NULL
)
{
app
=
prop
->
to_str
();
}
if
((
prop
=
obj
->
ensure_property_string
(
"stream"
))
!=
NULL
)
{
stream
=
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
);
}
if
(
plans
.
empty
())
{
ret
=
ERROR_HTTP_DVR_NO_TAEGET
;
srs_error
(
"dvr: rpc not found for url=%s/%s/%s, ret=%d"
,
vhost
.
c_str
(),
app
.
c_str
(),
stream
.
c_str
(),
ret
);
return
ret
;
}
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
()
{
source
=
NULL
;
...
...
trunk/src/app/srs_app_dvr.hpp
查看文件 @
fb3fced
...
...
@@ -277,48 +277,6 @@ public:
};
/**
* api plan: reap flv by api.
* @remark the api plan maybe create by publish event or http api post create dvr event.
* so when we got from pool first when create it.
*/
class
SrsDvrApiPlan
:
public
SrsDvrPlan
{
private
:
// cache the metadata and sequence header, for new segment maybe opened.
SrsSharedPtrMessage
*
sh_audio
;
SrsSharedPtrMessage
*
sh_video
;
SrsSharedPtrMessage
*
metadata
;
private
:
bool
autostart
;
bool
started
;
private
:
// user action, reap_segment.
std
::
string
action
;
std
::
string
path_template
;
public
:
SrsDvrApiPlan
();
virtual
~
SrsDvrApiPlan
();
public
:
virtual
int
initialize
(
SrsRequest
*
r
);
virtual
int
on_publish
();
virtual
void
on_unpublish
();
virtual
int
on_meta_data
(
SrsSharedPtrMessage
*
__metadata
);
virtual
int
on_audio
(
SrsSharedPtrMessage
*
__audio
);
virtual
int
on_video
(
SrsSharedPtrMessage
*
__video
);
public
:
virtual
int
set_plan
();
virtual
int
set_path_tmpl
(
std
::
string
path_tmpl
);
virtual
int
set_callback
(
std
::
string
value
);
virtual
int
set_wait_keyframe
(
bool
wait_keyframe
);
virtual
int
start
();
virtual
int
dumps
(
std
::
stringstream
&
ss
);
virtual
int
stop
();
virtual
int
rpc
(
SrsJsonObject
*
obj
);
private
:
virtual
int
check_user_actions
(
SrsSharedPtrMessage
*
msg
);
};
/**
* always append to flv file, never reap it.
*/
class
SrsDvrAppendPlan
:
public
SrsDvrPlan
...
...
@@ -363,30 +321,6 @@ private:
};
/**
* the api dvr pool.
*/
class
SrsApiDvrPool
{
private
:
std
::
vector
<
SrsDvrApiPlan
*>
dvrs
;
static
SrsApiDvrPool
*
_instance
;
private
:
SrsApiDvrPool
();
public
:
static
SrsApiDvrPool
*
instance
();
virtual
~
SrsApiDvrPool
();
public
:
virtual
SrsDvrApiPlan
*
get_dvr
(
std
::
string
vhost
);
virtual
int
add_dvr
(
SrsDvrApiPlan
*
dvr
);
virtual
void
detach_dvr
(
SrsDvrApiPlan
*
dvr
);
public
:
virtual
int
dumps
(
std
::
string
vhost
,
std
::
string
app
,
std
::
string
stream
,
std
::
stringstream
&
ss
);
virtual
int
create
(
SrsJsonAny
*
json
);
virtual
int
stop
(
std
::
string
vhost
,
std
::
string
app
,
std
::
string
stream
);
virtual
int
rpc
(
SrsJsonAny
*
json
);
};
/**
* dvr(digital video recorder) to record RTMP stream to flv file.
* TODO: FIXME: add utest for it.
*/
...
...
trunk/src/app/srs_app_http_api.cpp
查看文件 @
fb3fced
...
...
@@ -108,8 +108,7 @@ int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
<<
__SRS_JFIELD_STR
(
"authors"
,
"the primary authors and contributors"
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"requests"
,
"the request itself, for http debug"
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"vhosts"
,
"dumps vhost to json"
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"streams"
,
"dumps streams to json"
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_STR
(
"dvrs"
,
"query or control the dvr plan"
)
<<
__SRS_JFIELD_STR
(
"streams"
,
"dumps streams to json"
)
<<
__SRS_JOBJECT_END
<<
__SRS_JOBJECT_END
;
...
...
@@ -474,76 +473,6 @@ int SrsGoApiStreams::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
return
srs_go_http_response_json
(
w
,
ss
.
str
());
}
SrsGoApiDvrs
::
SrsGoApiDvrs
()
{
}
SrsGoApiDvrs
::~
SrsGoApiDvrs
()
{
}
int
SrsGoApiDvrs
::
serve_http
(
ISrsGoHttpResponseWriter
*
w
,
SrsHttpMessage
*
r
)
{
std
::
stringstream
ss
;
#ifndef SRS_AUTO_DVR
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ERROR_HTTP_DVR_DISABLED
)
<<
__SRS_JOBJECT_END
;
#else
SrsApiDvrPool
*
pool
=
SrsApiDvrPool
::
instance
();
if
(
r
->
is_http_get
())
{
std
::
stringstream
data
;
int
ret
=
pool
->
dumps
(
r
->
query_get
(
"vhost"
),
r
->
query_get
(
"app"
),
r
->
query_get
(
"stream"
),
data
);
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ret
)
<<
__SRS_JFIELD_CONT
<<
__SRS_JFIELD_ORG
(
"dvrs"
,
data
.
str
())
<<
__SRS_JOBJECT_END
;
}
else
if
(
r
->
is_http_post
())
{
std
::
string
body
=
r
->
body
();
SrsJsonAny
*
json
=
SrsJsonAny
::
loads
((
char
*
)
body
.
c_str
());
int
ret
=
ERROR_SUCCESS
;
if
(
!
json
)
{
ret
=
ERROR_HTTP_JSON_REQUIRED
;
}
else
{
SrsAutoFree
(
SrsJsonAny
,
json
);
ret
=
pool
->
create
(
json
);
}
ss
<<
__SRS_JOBJECT_START
<<
__SRS_JFIELD_ERROR
(
ret
)
<<
__SRS_JOBJECT_END
;
}
else
if
(
r
->
is_http_delete
())
{
int
ret
=
pool
->
stop
(
r
->
query_get
(
"vhost"
),
r
->
query_get
(
"app"
),
r
->
query_get
(
"stream"
));
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
)
<<
__SRS_JOBJECT_END
;
}
#endif
return
srs_go_http_response_json
(
w
,
ss
.
str
());
}
SrsHttpApi
::
SrsHttpApi
(
SrsServer
*
svr
,
st_netfd_t
fd
,
SrsGoHttpServeMux
*
m
)
:
SrsConnection
(
svr
,
fd
)
{
...
...
trunk/src/app/srs_app_http_api.hpp
查看文件 @
fb3fced
...
...
@@ -159,15 +159,6 @@ public:
virtual
int
serve_http
(
ISrsGoHttpResponseWriter
*
w
,
SrsHttpMessage
*
r
);
};
class
SrsGoApiDvrs
:
public
ISrsGoHttpHandler
{
public
:
SrsGoApiDvrs
();
virtual
~
SrsGoApiDvrs
();
public
:
virtual
int
serve_http
(
ISrsGoHttpResponseWriter
*
w
,
SrsHttpMessage
*
r
);
};
class
SrsHttpApi
:
public
SrsConnection
{
private
:
...
...
trunk/src/app/srs_app_server.cpp
查看文件 @
fb3fced
...
...
@@ -529,9 +529,6 @@ int SrsServer::initialize()
if
((
ret
=
http_api_mux
->
handle
(
"/api/v1/streams"
,
new
SrsGoApiStreams
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
((
ret
=
http_api_mux
->
handle
(
"/api/v1/dvrs"
,
new
SrsGoApiDvrs
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
#endif
#ifdef SRS_AUTO_HTTP_SERVER
...
...
trunk/src/core/srs_core.hpp
查看文件 @
fb3fced
...
...
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// current release version
#define VERSION_MAJOR 2
#define VERSION_MINOR 0
#define VERSION_REVISION 12
7
#define VERSION_REVISION 12
8
// server info.
#define RTMP_SIG_SRS_KEY "SRS"
...
...
请
注册
或
登录
后发表评论