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
2013-12-08 10:46:33 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
2887cda176b37403c33fbf6ff6945d6d024f5daa
2887cda1
1 parent
30099dfa
support multiple http hooks for a event.
隐藏空白字符变更
内嵌
并排对比
正在显示
3 个修改的文件
包含
1980 行增加
和
1980 行删除
trunk/src/core/srs_core_client.cpp
trunk/src/core/srs_core_config.cpp
trunk/src/core/srs_core_config.hpp
trunk/src/core/srs_core_client.cpp
100755 → 100644
查看文件 @
2887cda
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_client.hpp>
#include <arpa/inet.h>
#include <stdlib.h>
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_rtmp.hpp>
#include <srs_core_protocol.hpp>
#include <srs_core_autofree.hpp>
#include <srs_core_source.hpp>
#include <srs_core_server.hpp>
#include <srs_core_pithy_print.hpp>
#include <srs_core_config.hpp>
#include <srs_core_refer.hpp>
#include <srs_core_hls.hpp>
#include <srs_core_http.hpp>
#define SRS_PULSE_TIMEOUT_MS 100
#define SRS_SEND_TIMEOUT_US 5000000L
#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
#define SRS_STREAM_BUSY_SLEEP_MS 2000
SrsClient
::
SrsClient
(
SrsServer
*
srs_server
,
st_netfd_t
client_stfd
)
:
SrsConnection
(
srs_server
,
client_stfd
)
{
ip
=
NULL
;
req
=
new
SrsRequest
();
res
=
new
SrsResponse
();
rtmp
=
new
SrsRtmp
(
client_stfd
);
refer
=
new
SrsRefer
();
#ifdef SRS_HTTP
http_hooks
=
new
SrsHttpHooks
();
#endif
}
SrsClient
::~
SrsClient
()
{
srs_freepa
(
ip
);
srs_freep
(
req
);
srs_freep
(
res
);
srs_freep
(
rtmp
);
srs_freep
(
refer
);
#ifdef SRS_HTTP
srs_freep
(
http_hooks
);
#endif
}
// TODO: return detail message when error for client.
int
SrsClient
::
do_cycle
()
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
get_peer_ip
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"get peer ip failed. ret=%d"
,
ret
);
return
ret
;
}
srs_trace
(
"get peer ip success. ip=%s, send_to=%"
PRId64
", recv_to=%"
PRId64
""
,
ip
,
SRS_SEND_TIMEOUT_US
,
SRS_RECV_TIMEOUT_US
);
rtmp
->
set_recv_timeout
(
SRS_RECV_TIMEOUT_US
);
rtmp
->
set_send_timeout
(
SRS_SEND_TIMEOUT_US
);
if
((
ret
=
rtmp
->
handshake
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp handshake failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"rtmp handshake success"
);
if
((
ret
=
rtmp
->
connect_app
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp connect vhost/app failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"rtmp connect app success"
);
if
((
ret
=
check_vhost
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"check vhost failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check vhost success."
);
srs_trace
(
"rtmp connect app success. "
"tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s"
,
req
->
tcUrl
.
c_str
(),
req
->
pageUrl
.
c_str
(),
req
->
swfUrl
.
c_str
(),
req
->
schema
.
c_str
(),
req
->
vhost
.
c_str
(),
req
->
port
.
c_str
(),
req
->
app
.
c_str
());
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check refer success."
);
if
((
ret
=
rtmp
->
set_window_ack_size
(
2.5
*
1000
*
1000
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set window acknowledgement size failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"set window acknowledgement size success"
);
if
((
ret
=
rtmp
->
set_peer_bandwidth
(
2.5
*
1000
*
1000
,
2
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set peer bandwidth failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"set peer bandwidth success"
);
if
((
ret
=
rtmp
->
response_connect_app
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"response connect app failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"response connect app success"
);
if
((
ret
=
rtmp
->
on_bw_done
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"on_bw_done failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"on_bw_done success"
);
SrsClientType
type
;
if
((
ret
=
rtmp
->
identify_client
(
res
->
stream_id
,
type
,
req
->
stream
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"identify client failed. ret=%d"
,
ret
);
return
ret
;
}
req
->
strip
();
srs_trace
(
"identify client success. type=%d, stream_name=%s"
,
type
,
req
->
stream
.
c_str
());
int
chunk_size
=
4096
;
SrsConfDirective
*
conf
=
config
->
get_chunk_size
();
if
(
conf
&&
!
conf
->
arg0
().
empty
())
{
chunk_size
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
((
ret
=
rtmp
->
set_chunk_size
(
chunk_size
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set chunk_size=%d failed. ret=%d"
,
chunk_size
,
ret
);
return
ret
;
}
srs_trace
(
"set chunk_size=%d success"
,
chunk_size
);
// find a source to publish.
SrsSource
*
source
=
SrsSource
::
find
(
req
->
get_stream_url
());
srs_assert
(
source
!=
NULL
);
// check publish available.
if
(
type
!=
SrsClientPlay
&&
!
source
->
can_publish
())
{
ret
=
ERROR_SYSTEM_STREAM_BUSY
;
srs_warn
(
"stream %s is already publishing. ret=%d"
,
req
->
get_stream_url
().
c_str
(),
ret
);
// to delay request
st_usleep
(
SRS_STREAM_BUSY_SLEEP_MS
*
1000
);
return
ret
;
}
bool
enabled_cache
=
true
;
conf
=
config
->
get_gop_cache
(
req
->
vhost
);
if
(
conf
&&
conf
->
arg0
()
==
"off"
)
{
enabled_cache
=
false
;
}
source
->
set_cache
(
enabled_cache
);
srs_info
(
"source found, url=%s, enabled_cache=%d"
,
req
->
get_stream_url
().
c_str
(),
enabled_cache
);
switch
(
type
)
{
case
SrsClientPlay
:
{
srs_verbose
(
"start to play stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_play
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"start to play stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"start to play stream %s success"
,
req
->
stream
.
c_str
());
return
playing
(
source
);
}
case
SrsClientFMLEPublish
:
{
srs_verbose
(
"FMLE start to publish stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_fmle_publish
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"start to publish stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"start to publish stream %s success"
,
req
->
stream
.
c_str
());
ret
=
publish
(
source
,
true
);
source
->
on_unpublish
();
return
ret
;
}
case
SrsClientFlashPublish
:
{
srs_verbose
(
"flash start to publish stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_flash_publish
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"flash start to publish stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"flash start to publish stream %s success"
,
req
->
stream
.
c_str
());
ret
=
publish
(
source
,
false
);
source
->
on_unpublish
();
return
ret
;
}
default
:
{
ret
=
ERROR_SYSTEM_CLIENT_INVALID
;
srs_info
(
"invalid client type=%d. ret=%d"
,
type
,
ret
);
return
ret
;
}
}
return
ret
;
}
int
SrsClient
::
check_vhost
()
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
req
!=
NULL
);
SrsConfDirective
*
vhost
=
config
->
get_vhost
(
req
->
vhost
);
if
(
vhost
==
NULL
)
{
ret
=
ERROR_RTMP_VHOST_NOT_FOUND
;
srs_error
(
"vhost %s not found. ret=%d"
,
req
->
vhost
.
c_str
(),
ret
);
return
ret
;
}
if
(
!
config
->
get_vhost_enabled
(
req
->
vhost
))
{
ret
=
ERROR_RTMP_VHOST_NOT_FOUND
;
srs_error
(
"vhost %s disabled. ret=%d"
,
req
->
vhost
.
c_str
(),
ret
);
return
ret
;
}
if
(
req
->
vhost
!=
vhost
->
arg0
())
{
srs_trace
(
"vhost change from %s to %s"
,
req
->
vhost
.
c_str
(),
vhost
->
arg0
().
c_str
());
req
->
vhost
=
vhost
->
arg0
();
}
#ifdef SRS_HTTP
// HTTP: on_connect
SrsConfDirective
*
on_connect
=
config
->
get_vhost_on_connect
(
req
->
vhost
);
if
(
!
on_connect
)
{
srs_info
(
"ignore the empty http callback: on_connect"
);
return
ret
;
}
for
(
int
i
=
0
;
i
<
(
int
)
on_connect
->
args
.
size
();
i
++
)
{
std
::
string
url
=
on_connect
->
args
.
at
(
i
);
if
((
ret
=
http_hooks
->
on_connect
(
url
,
ip
,
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hook client failed. url=%s, ret=%d"
,
url
.
c_str
(),
ret
);
return
ret
;
}
}
#endif
return
ret
;
}
int
SrsClient
::
playing
(
SrsSource
*
source
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer_play
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check play_refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check play_refer success."
);
SrsConsumer
*
consumer
=
NULL
;
if
((
ret
=
source
->
create_consumer
(
consumer
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"create consumer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_assert
(
consumer
!=
NULL
);
SrsAutoFree
(
SrsConsumer
,
consumer
,
false
);
srs_verbose
(
"consumer created success."
);
rtmp
->
set_recv_timeout
(
SRS_PULSE_TIMEOUT_MS
*
1000
);
SrsPithyPrint
pithy_print
(
SRS_STAGE_PLAY_USER
);
while
(
true
)
{
pithy_print
.
elapse
(
SRS_PULSE_TIMEOUT_MS
);
// switch to other st-threads.
st_usleep
(
0
);
// read from client.
int
ctl_msg_ret
=
ERROR_SUCCESS
;
if
(
true
)
{
SrsCommonMessage
*
msg
=
NULL
;
ctl_msg_ret
=
ret
=
rtmp
->
recv_message
(
&
msg
);
srs_verbose
(
"play loop recv message. ret=%d"
,
ret
);
if
(
ret
!=
ERROR_SUCCESS
&&
ret
!=
ERROR_SOCKET_TIMEOUT
)
{
srs_error
(
"recv client control message failed. ret=%d"
,
ret
);
return
ret
;
}
if
((
ret
=
process_play_control_msg
(
consumer
,
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"process play control message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// get messages from consumer.
SrsSharedPtrMessage
**
msgs
=
NULL
;
int
count
=
0
;
if
((
ret
=
consumer
->
get_packets
(
0
,
msgs
,
count
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"get messages from consumer failed. ret=%d"
,
ret
);
return
ret
;
}
// reportable
if
(
pithy_print
.
can_print
())
{
srs_trace
(
"-> time=%"
PRId64
", cmr=%d, msgs=%d, obytes=%"
PRId64
", ibytes=%"
PRId64
", okbps=%d, ikbps=%d"
,
pithy_print
.
get_age
(),
ctl_msg_ret
,
count
,
rtmp
->
get_send_bytes
(),
rtmp
->
get_recv_bytes
(),
rtmp
->
get_send_kbps
(),
rtmp
->
get_recv_kbps
());
}
if
(
count
<=
0
)
{
srs_verbose
(
"no packets in queue."
);
continue
;
}
SrsAutoFree
(
SrsSharedPtrMessage
*
,
msgs
,
true
);
// sendout messages
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
SrsSharedPtrMessage
*
msg
=
msgs
[
i
];
// the send_message will free the msg,
// so set the msgs[i] to NULL.
msgs
[
i
]
=
NULL
;
if
((
ret
=
rtmp
->
send_message
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"send message to client failed. ret=%d"
,
ret
);
return
ret
;
}
}
}
return
ret
;
}
int
SrsClient
::
publish
(
SrsSource
*
source
,
bool
is_fmle
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer_publish
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check publish_refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check publish_refer success."
);
SrsPithyPrint
pithy_print
(
SRS_STAGE_PUBLISH_USER
);
// notify the hls to prepare when publish start.
if
((
ret
=
source
->
on_publish
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hls on_publish failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"hls on_publish success."
);
while
(
true
)
{
// switch to other st-threads.
st_usleep
(
0
);
SrsCommonMessage
*
msg
=
NULL
;
if
((
ret
=
rtmp
->
recv_message
(
&
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"recv identify client message failed. ret=%d"
,
ret
);
return
ret
;
}
SrsAutoFree
(
SrsCommonMessage
,
msg
,
false
);
pithy_print
.
set_age
(
msg
->
header
.
timestamp
);
// reportable
if
(
pithy_print
.
can_print
())
{
srs_trace
(
"<- time=%"
PRId64
", obytes=%"
PRId64
", ibytes=%"
PRId64
", okbps=%d, ikbps=%d"
,
pithy_print
.
get_age
(),
rtmp
->
get_send_bytes
(),
rtmp
->
get_recv_bytes
(),
rtmp
->
get_send_kbps
(),
rtmp
->
get_recv_kbps
());
}
if
((
ret
=
process_publish_message
(
source
,
msg
,
is_fmle
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"process publish message failed. ret=%d"
,
ret
);
return
ret
;
}
}
return
ret
;
}
int
SrsClient
::
process_publish_message
(
SrsSource
*
source
,
SrsCommonMessage
*
msg
,
bool
is_fmle
)
{
int
ret
=
ERROR_SUCCESS
;
// process audio packet
if
(
msg
->
header
.
is_audio
())
{
if
((
ret
=
source
->
on_audio
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process audio message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// process video packet
if
(
msg
->
header
.
is_video
())
{
if
((
ret
=
source
->
on_video
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process video message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// process onMetaData
if
(
msg
->
header
.
is_amf0_data
()
||
msg
->
header
.
is_amf3_data
())
{
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode onMetaData message failed. ret=%d"
,
ret
);
return
ret
;
}
SrsPacket
*
pkt
=
msg
->
get_packet
();
if
(
dynamic_cast
<
SrsOnMetaDataPacket
*>
(
pkt
))
{
SrsOnMetaDataPacket
*
metadata
=
dynamic_cast
<
SrsOnMetaDataPacket
*>
(
pkt
);
if
((
ret
=
source
->
on_meta_data
(
msg
,
metadata
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process onMetaData message failed. ret=%d"
,
ret
);
return
ret
;
}
srs_trace
(
"process onMetaData message success."
);
return
ret
;
}
srs_trace
(
"ignore AMF0/AMF3 data message."
);
return
ret
;
}
// process UnPublish event.
if
(
msg
->
header
.
is_amf0_command
()
||
msg
->
header
.
is_amf3_command
())
{
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode unpublish message failed. ret=%d"
,
ret
);
return
ret
;
}
// flash unpublish.
if
(
!
is_fmle
)
{
srs_trace
(
"flash publish finished."
);
return
ret
;
}
SrsPacket
*
pkt
=
msg
->
get_packet
();
if
(
dynamic_cast
<
SrsFMLEStartPacket
*>
(
pkt
))
{
SrsFMLEStartPacket
*
unpublish
=
dynamic_cast
<
SrsFMLEStartPacket
*>
(
pkt
);
return
rtmp
->
fmle_unpublish
(
res
->
stream_id
,
unpublish
->
transaction_id
);
}
srs_trace
(
"ignore AMF0/AMF3 command message."
);
return
ret
;
}
return
ret
;
}
int
SrsClient
::
get_peer_ip
()
{
int
ret
=
ERROR_SUCCESS
;
int
fd
=
st_netfd_fileno
(
stfd
);
// discovery client information
sockaddr_in
addr
;
socklen_t
addrlen
=
sizeof
(
addr
);
if
(
getpeername
(
fd
,
(
sockaddr
*
)
&
addr
,
&
addrlen
)
==
-
1
)
{
ret
=
ERROR_SOCKET_GET_PEER_NAME
;
srs_error
(
"discovery client information failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"get peer name success."
);
// ip v4 or v6
char
buf
[
INET6_ADDRSTRLEN
];
memset
(
buf
,
0
,
sizeof
(
buf
));
if
((
inet_ntop
(
addr
.
sin_family
,
&
addr
.
sin_addr
,
buf
,
sizeof
(
buf
)))
==
NULL
)
{
ret
=
ERROR_SOCKET_GET_PEER_IP
;
srs_error
(
"convert client information failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"get peer ip of client ip=%s, fd=%d"
,
buf
,
fd
);
ip
=
new
char
[
strlen
(
buf
)
+
1
];
strcpy
(
ip
,
buf
);
srs_verbose
(
"get peer ip success. ip=%s, fd=%d"
,
ip
,
fd
);
return
ret
;
}
int
SrsClient
::
process_play_control_msg
(
SrsConsumer
*
consumer
,
SrsCommonMessage
*
msg
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
msg
)
{
srs_verbose
(
"ignore all empty message."
);
return
ret
;
}
SrsAutoFree
(
SrsCommonMessage
,
msg
,
false
);
if
(
!
msg
->
header
.
is_amf0_command
()
&&
!
msg
->
header
.
is_amf3_command
())
{
srs_info
(
"ignore all message except amf0/amf3 command."
);
return
ret
;
}
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode the amf0/amf3 command packet failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"decode the amf0/amf3 command packet success."
);
SrsPausePacket
*
pause
=
dynamic_cast
<
SrsPausePacket
*>
(
msg
->
get_packet
());
if
(
!
pause
)
{
srs_info
(
"ignore all amf0/amf3 command except pause."
);
return
ret
;
}
if
((
ret
=
rtmp
->
on_play_client_pause
(
res
->
stream_id
,
pause
->
is_pause
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp process play client pause failed. ret=%d"
,
ret
);
return
ret
;
}
if
((
ret
=
consumer
->
on_play_client_pause
(
pause
->
is_pause
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"consumer process play client pause failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"process pause success, is_pause=%d, time=%d."
,
pause
->
is_pause
,
pause
->
time_ms
);
return
ret
;
}
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_client.hpp>
#include <arpa/inet.h>
#include <stdlib.h>
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_rtmp.hpp>
#include <srs_core_protocol.hpp>
#include <srs_core_autofree.hpp>
#include <srs_core_source.hpp>
#include <srs_core_server.hpp>
#include <srs_core_pithy_print.hpp>
#include <srs_core_config.hpp>
#include <srs_core_refer.hpp>
#include <srs_core_hls.hpp>
#include <srs_core_http.hpp>
#define SRS_PULSE_TIMEOUT_MS 100
#define SRS_SEND_TIMEOUT_US 5000000L
#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
#define SRS_STREAM_BUSY_SLEEP_MS 2000
SrsClient
::
SrsClient
(
SrsServer
*
srs_server
,
st_netfd_t
client_stfd
)
:
SrsConnection
(
srs_server
,
client_stfd
)
{
ip
=
NULL
;
req
=
new
SrsRequest
();
res
=
new
SrsResponse
();
rtmp
=
new
SrsRtmp
(
client_stfd
);
refer
=
new
SrsRefer
();
#ifdef SRS_HTTP
http_hooks
=
new
SrsHttpHooks
();
#endif
}
SrsClient
::~
SrsClient
()
{
srs_freepa
(
ip
);
srs_freep
(
req
);
srs_freep
(
res
);
srs_freep
(
rtmp
);
srs_freep
(
refer
);
#ifdef SRS_HTTP
srs_freep
(
http_hooks
);
#endif
}
// TODO: return detail message when error for client.
int
SrsClient
::
do_cycle
()
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
get_peer_ip
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"get peer ip failed. ret=%d"
,
ret
);
return
ret
;
}
srs_trace
(
"get peer ip success. ip=%s, send_to=%"
PRId64
", recv_to=%"
PRId64
""
,
ip
,
SRS_SEND_TIMEOUT_US
,
SRS_RECV_TIMEOUT_US
);
rtmp
->
set_recv_timeout
(
SRS_RECV_TIMEOUT_US
);
rtmp
->
set_send_timeout
(
SRS_SEND_TIMEOUT_US
);
if
((
ret
=
rtmp
->
handshake
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp handshake failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"rtmp handshake success"
);
if
((
ret
=
rtmp
->
connect_app
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp connect vhost/app failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"rtmp connect app success"
);
if
((
ret
=
check_vhost
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"check vhost failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check vhost success."
);
srs_trace
(
"rtmp connect app success. "
"tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s"
,
req
->
tcUrl
.
c_str
(),
req
->
pageUrl
.
c_str
(),
req
->
swfUrl
.
c_str
(),
req
->
schema
.
c_str
(),
req
->
vhost
.
c_str
(),
req
->
port
.
c_str
(),
req
->
app
.
c_str
());
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check refer success."
);
if
((
ret
=
rtmp
->
set_window_ack_size
(
2.5
*
1000
*
1000
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set window acknowledgement size failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"set window acknowledgement size success"
);
if
((
ret
=
rtmp
->
set_peer_bandwidth
(
2.5
*
1000
*
1000
,
2
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set peer bandwidth failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"set peer bandwidth success"
);
if
((
ret
=
rtmp
->
response_connect_app
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"response connect app failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"response connect app success"
);
if
((
ret
=
rtmp
->
on_bw_done
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"on_bw_done failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"on_bw_done success"
);
SrsClientType
type
;
if
((
ret
=
rtmp
->
identify_client
(
res
->
stream_id
,
type
,
req
->
stream
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"identify client failed. ret=%d"
,
ret
);
return
ret
;
}
req
->
strip
();
srs_trace
(
"identify client success. type=%d, stream_name=%s"
,
type
,
req
->
stream
.
c_str
());
int
chunk_size
=
4096
;
SrsConfDirective
*
conf
=
config
->
get_chunk_size
();
if
(
conf
&&
!
conf
->
arg0
().
empty
())
{
chunk_size
=
::
atoi
(
conf
->
arg0
().
c_str
());
}
if
((
ret
=
rtmp
->
set_chunk_size
(
chunk_size
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"set chunk_size=%d failed. ret=%d"
,
chunk_size
,
ret
);
return
ret
;
}
srs_trace
(
"set chunk_size=%d success"
,
chunk_size
);
// find a source to publish.
SrsSource
*
source
=
SrsSource
::
find
(
req
->
get_stream_url
());
srs_assert
(
source
!=
NULL
);
// check publish available.
if
(
type
!=
SrsClientPlay
&&
!
source
->
can_publish
())
{
ret
=
ERROR_SYSTEM_STREAM_BUSY
;
srs_warn
(
"stream %s is already publishing. ret=%d"
,
req
->
get_stream_url
().
c_str
(),
ret
);
// to delay request
st_usleep
(
SRS_STREAM_BUSY_SLEEP_MS
*
1000
);
return
ret
;
}
bool
enabled_cache
=
true
;
conf
=
config
->
get_gop_cache
(
req
->
vhost
);
if
(
conf
&&
conf
->
arg0
()
==
"off"
)
{
enabled_cache
=
false
;
}
source
->
set_cache
(
enabled_cache
);
srs_info
(
"source found, url=%s, enabled_cache=%d"
,
req
->
get_stream_url
().
c_str
(),
enabled_cache
);
switch
(
type
)
{
case
SrsClientPlay
:
{
srs_verbose
(
"start to play stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_play
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"start to play stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"start to play stream %s success"
,
req
->
stream
.
c_str
());
return
playing
(
source
);
}
case
SrsClientFMLEPublish
:
{
srs_verbose
(
"FMLE start to publish stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_fmle_publish
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"start to publish stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"start to publish stream %s success"
,
req
->
stream
.
c_str
());
ret
=
publish
(
source
,
true
);
source
->
on_unpublish
();
return
ret
;
}
case
SrsClientFlashPublish
:
{
srs_verbose
(
"flash start to publish stream %s."
,
req
->
stream
.
c_str
());
if
((
ret
=
rtmp
->
start_flash_publish
(
res
->
stream_id
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"flash start to publish stream failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"flash start to publish stream %s success"
,
req
->
stream
.
c_str
());
ret
=
publish
(
source
,
false
);
source
->
on_unpublish
();
return
ret
;
}
default
:
{
ret
=
ERROR_SYSTEM_CLIENT_INVALID
;
srs_info
(
"invalid client type=%d. ret=%d"
,
type
,
ret
);
return
ret
;
}
}
return
ret
;
}
int
SrsClient
::
check_vhost
()
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
req
!=
NULL
);
SrsConfDirective
*
vhost
=
config
->
get_vhost
(
req
->
vhost
);
if
(
vhost
==
NULL
)
{
ret
=
ERROR_RTMP_VHOST_NOT_FOUND
;
srs_error
(
"vhost %s not found. ret=%d"
,
req
->
vhost
.
c_str
(),
ret
);
return
ret
;
}
if
(
!
config
->
get_vhost_enabled
(
req
->
vhost
))
{
ret
=
ERROR_RTMP_VHOST_NOT_FOUND
;
srs_error
(
"vhost %s disabled. ret=%d"
,
req
->
vhost
.
c_str
(),
ret
);
return
ret
;
}
if
(
req
->
vhost
!=
vhost
->
arg0
())
{
srs_trace
(
"vhost change from %s to %s"
,
req
->
vhost
.
c_str
(),
vhost
->
arg0
().
c_str
());
req
->
vhost
=
vhost
->
arg0
();
}
#ifdef SRS_HTTP
// HTTP: on_connect
SrsConfDirective
*
on_connect
=
config
->
get_vhost_on_connect
(
req
->
vhost
);
if
(
!
on_connect
)
{
srs_info
(
"ignore the empty http callback: on_connect"
);
return
ret
;
}
for
(
int
i
=
0
;
i
<
(
int
)
on_connect
->
args
.
size
();
i
++
)
{
std
::
string
url
=
on_connect
->
args
.
at
(
i
);
if
((
ret
=
http_hooks
->
on_connect
(
url
,
ip
,
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hook client failed. url=%s, ret=%d"
,
url
.
c_str
(),
ret
);
return
ret
;
}
}
#endif
return
ret
;
}
int
SrsClient
::
playing
(
SrsSource
*
source
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer_play
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check play_refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check play_refer success."
);
SrsConsumer
*
consumer
=
NULL
;
if
((
ret
=
source
->
create_consumer
(
consumer
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"create consumer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_assert
(
consumer
!=
NULL
);
SrsAutoFree
(
SrsConsumer
,
consumer
,
false
);
srs_verbose
(
"consumer created success."
);
rtmp
->
set_recv_timeout
(
SRS_PULSE_TIMEOUT_MS
*
1000
);
SrsPithyPrint
pithy_print
(
SRS_STAGE_PLAY_USER
);
while
(
true
)
{
pithy_print
.
elapse
(
SRS_PULSE_TIMEOUT_MS
);
// switch to other st-threads.
st_usleep
(
0
);
// read from client.
int
ctl_msg_ret
=
ERROR_SUCCESS
;
if
(
true
)
{
SrsCommonMessage
*
msg
=
NULL
;
ctl_msg_ret
=
ret
=
rtmp
->
recv_message
(
&
msg
);
srs_verbose
(
"play loop recv message. ret=%d"
,
ret
);
if
(
ret
!=
ERROR_SUCCESS
&&
ret
!=
ERROR_SOCKET_TIMEOUT
)
{
srs_error
(
"recv client control message failed. ret=%d"
,
ret
);
return
ret
;
}
if
((
ret
=
process_play_control_msg
(
consumer
,
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"process play control message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// get messages from consumer.
SrsSharedPtrMessage
**
msgs
=
NULL
;
int
count
=
0
;
if
((
ret
=
consumer
->
get_packets
(
0
,
msgs
,
count
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"get messages from consumer failed. ret=%d"
,
ret
);
return
ret
;
}
// reportable
if
(
pithy_print
.
can_print
())
{
srs_trace
(
"-> time=%"
PRId64
", cmr=%d, msgs=%d, obytes=%"
PRId64
", ibytes=%"
PRId64
", okbps=%d, ikbps=%d"
,
pithy_print
.
get_age
(),
ctl_msg_ret
,
count
,
rtmp
->
get_send_bytes
(),
rtmp
->
get_recv_bytes
(),
rtmp
->
get_send_kbps
(),
rtmp
->
get_recv_kbps
());
}
if
(
count
<=
0
)
{
srs_verbose
(
"no packets in queue."
);
continue
;
}
SrsAutoFree
(
SrsSharedPtrMessage
*
,
msgs
,
true
);
// sendout messages
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
SrsSharedPtrMessage
*
msg
=
msgs
[
i
];
// the send_message will free the msg,
// so set the msgs[i] to NULL.
msgs
[
i
]
=
NULL
;
if
((
ret
=
rtmp
->
send_message
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"send message to client failed. ret=%d"
,
ret
);
return
ret
;
}
}
}
return
ret
;
}
int
SrsClient
::
publish
(
SrsSource
*
source
,
bool
is_fmle
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
refer
->
check
(
req
->
pageUrl
,
config
->
get_refer_publish
(
req
->
vhost
)))
!=
ERROR_SUCCESS
)
{
srs_error
(
"check publish_refer failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"check publish_refer success."
);
SrsPithyPrint
pithy_print
(
SRS_STAGE_PUBLISH_USER
);
// notify the hls to prepare when publish start.
if
((
ret
=
source
->
on_publish
(
req
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"hls on_publish failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"hls on_publish success."
);
while
(
true
)
{
// switch to other st-threads.
st_usleep
(
0
);
SrsCommonMessage
*
msg
=
NULL
;
if
((
ret
=
rtmp
->
recv_message
(
&
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"recv identify client message failed. ret=%d"
,
ret
);
return
ret
;
}
SrsAutoFree
(
SrsCommonMessage
,
msg
,
false
);
pithy_print
.
set_age
(
msg
->
header
.
timestamp
);
// reportable
if
(
pithy_print
.
can_print
())
{
srs_trace
(
"<- time=%"
PRId64
", obytes=%"
PRId64
", ibytes=%"
PRId64
", okbps=%d, ikbps=%d"
,
pithy_print
.
get_age
(),
rtmp
->
get_send_bytes
(),
rtmp
->
get_recv_bytes
(),
rtmp
->
get_send_kbps
(),
rtmp
->
get_recv_kbps
());
}
if
((
ret
=
process_publish_message
(
source
,
msg
,
is_fmle
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"process publish message failed. ret=%d"
,
ret
);
return
ret
;
}
}
return
ret
;
}
int
SrsClient
::
process_publish_message
(
SrsSource
*
source
,
SrsCommonMessage
*
msg
,
bool
is_fmle
)
{
int
ret
=
ERROR_SUCCESS
;
// process audio packet
if
(
msg
->
header
.
is_audio
())
{
if
((
ret
=
source
->
on_audio
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process audio message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// process video packet
if
(
msg
->
header
.
is_video
())
{
if
((
ret
=
source
->
on_video
(
msg
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process video message failed. ret=%d"
,
ret
);
return
ret
;
}
}
// process onMetaData
if
(
msg
->
header
.
is_amf0_data
()
||
msg
->
header
.
is_amf3_data
())
{
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode onMetaData message failed. ret=%d"
,
ret
);
return
ret
;
}
SrsPacket
*
pkt
=
msg
->
get_packet
();
if
(
dynamic_cast
<
SrsOnMetaDataPacket
*>
(
pkt
))
{
SrsOnMetaDataPacket
*
metadata
=
dynamic_cast
<
SrsOnMetaDataPacket
*>
(
pkt
);
if
((
ret
=
source
->
on_meta_data
(
msg
,
metadata
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"source process onMetaData message failed. ret=%d"
,
ret
);
return
ret
;
}
srs_trace
(
"process onMetaData message success."
);
return
ret
;
}
srs_trace
(
"ignore AMF0/AMF3 data message."
);
return
ret
;
}
// process UnPublish event.
if
(
msg
->
header
.
is_amf0_command
()
||
msg
->
header
.
is_amf3_command
())
{
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode unpublish message failed. ret=%d"
,
ret
);
return
ret
;
}
// flash unpublish.
if
(
!
is_fmle
)
{
srs_trace
(
"flash publish finished."
);
return
ret
;
}
SrsPacket
*
pkt
=
msg
->
get_packet
();
if
(
dynamic_cast
<
SrsFMLEStartPacket
*>
(
pkt
))
{
SrsFMLEStartPacket
*
unpublish
=
dynamic_cast
<
SrsFMLEStartPacket
*>
(
pkt
);
return
rtmp
->
fmle_unpublish
(
res
->
stream_id
,
unpublish
->
transaction_id
);
}
srs_trace
(
"ignore AMF0/AMF3 command message."
);
return
ret
;
}
return
ret
;
}
int
SrsClient
::
get_peer_ip
()
{
int
ret
=
ERROR_SUCCESS
;
int
fd
=
st_netfd_fileno
(
stfd
);
// discovery client information
sockaddr_in
addr
;
socklen_t
addrlen
=
sizeof
(
addr
);
if
(
getpeername
(
fd
,
(
sockaddr
*
)
&
addr
,
&
addrlen
)
==
-
1
)
{
ret
=
ERROR_SOCKET_GET_PEER_NAME
;
srs_error
(
"discovery client information failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"get peer name success."
);
// ip v4 or v6
char
buf
[
INET6_ADDRSTRLEN
];
memset
(
buf
,
0
,
sizeof
(
buf
));
if
((
inet_ntop
(
addr
.
sin_family
,
&
addr
.
sin_addr
,
buf
,
sizeof
(
buf
)))
==
NULL
)
{
ret
=
ERROR_SOCKET_GET_PEER_IP
;
srs_error
(
"convert client information failed. ret=%d"
,
ret
);
return
ret
;
}
srs_verbose
(
"get peer ip of client ip=%s, fd=%d"
,
buf
,
fd
);
ip
=
new
char
[
strlen
(
buf
)
+
1
];
strcpy
(
ip
,
buf
);
srs_verbose
(
"get peer ip success. ip=%s, fd=%d"
,
ip
,
fd
);
return
ret
;
}
int
SrsClient
::
process_play_control_msg
(
SrsConsumer
*
consumer
,
SrsCommonMessage
*
msg
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
msg
)
{
srs_verbose
(
"ignore all empty message."
);
return
ret
;
}
SrsAutoFree
(
SrsCommonMessage
,
msg
,
false
);
if
(
!
msg
->
header
.
is_amf0_command
()
&&
!
msg
->
header
.
is_amf3_command
())
{
srs_info
(
"ignore all message except amf0/amf3 command."
);
return
ret
;
}
if
((
ret
=
msg
->
decode_packet
(
rtmp
->
get_protocol
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"decode the amf0/amf3 command packet failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"decode the amf0/amf3 command packet success."
);
SrsPausePacket
*
pause
=
dynamic_cast
<
SrsPausePacket
*>
(
msg
->
get_packet
());
if
(
!
pause
)
{
srs_info
(
"ignore all amf0/amf3 command except pause."
);
return
ret
;
}
if
((
ret
=
rtmp
->
on_play_client_pause
(
res
->
stream_id
,
pause
->
is_pause
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"rtmp process play client pause failed. ret=%d"
,
ret
);
return
ret
;
}
if
((
ret
=
consumer
->
on_play_client_pause
(
pause
->
is_pause
))
!=
ERROR_SUCCESS
)
{
srs_error
(
"consumer process play client pause failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"process pause success, is_pause=%d, time=%d."
,
pause
->
is_pause
,
pause
->
time_ms
);
return
ret
;
}
...
...
trunk/src/core/srs_core_config.cpp
100755 → 100644
查看文件 @
2887cda
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_config.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// file operations.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_autofree.hpp>
#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
int64_t
FILE_SIZE
(
int
fd
)
{
int64_t
pre
=
FILE_OFFSET
(
fd
);
int64_t
pos
=
lseek
(
fd
,
0
,
SEEK_END
);
lseek
(
fd
,
pre
,
SEEK_SET
);
return
pos
;
}
#define LF (char)0x0a
#define CR (char)0x0d
bool
is_common_space
(
char
ch
)
{
return
(
ch
==
' '
||
ch
==
'\t'
||
ch
==
CR
||
ch
==
LF
);
}
#define CONF_BUFFER_SIZE 1024 * 1024
SrsFileBuffer
::
SrsFileBuffer
()
{
fd
=
-
1
;
line
=
0
;
pos
=
last
=
start
=
new
char
[
CONF_BUFFER_SIZE
];
end
=
start
+
CONF_BUFFER_SIZE
;
}
SrsFileBuffer
::~
SrsFileBuffer
()
{
if
(
fd
>
0
)
{
close
(
fd
);
}
srs_freepa
(
start
);
}
int
SrsFileBuffer
::
open
(
const
char
*
filename
)
{
assert
(
fd
==
-
1
);
if
((
fd
=
::
open
(
filename
,
O_RDONLY
,
0
))
<
0
)
{
srs_error
(
"open conf file error. errno=%d(%s)"
,
errno
,
strerror
(
errno
));
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
line
=
1
;
return
ERROR_SUCCESS
;
}
SrsConfDirective
::
SrsConfDirective
()
{
}
SrsConfDirective
::~
SrsConfDirective
()
{
std
::
vector
<
SrsConfDirective
*>::
iterator
it
;
for
(
it
=
directives
.
begin
();
it
!=
directives
.
end
();
++
it
)
{
SrsConfDirective
*
directive
=
*
it
;
srs_freep
(
directive
);
}
directives
.
clear
();
}
std
::
string
SrsConfDirective
::
arg0
()
{
if
(
args
.
size
()
>
0
)
{
return
args
.
at
(
0
);
}
return
""
;
}
std
::
string
SrsConfDirective
::
arg1
()
{
if
(
args
.
size
()
>
1
)
{
return
args
.
at
(
1
);
}
return
""
;
}
std
::
string
SrsConfDirective
::
arg2
()
{
if
(
args
.
size
()
>
2
)
{
return
args
.
at
(
2
);
}
return
""
;
}
SrsConfDirective
*
SrsConfDirective
::
at
(
int
index
)
{
return
directives
.
at
(
index
);
}
SrsConfDirective
*
SrsConfDirective
::
get
(
std
::
string
_name
)
{
std
::
vector
<
SrsConfDirective
*>::
iterator
it
;
for
(
it
=
directives
.
begin
();
it
!=
directives
.
end
();
++
it
)
{
SrsConfDirective
*
directive
=
*
it
;
if
(
directive
->
name
==
_name
)
{
return
directive
;
}
}
return
NULL
;
}
int
SrsConfDirective
::
parse
(
const
char
*
filename
)
{
int
ret
=
ERROR_SUCCESS
;
SrsFileBuffer
buffer
;
if
((
ret
=
buffer
.
open
(
filename
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
parse_conf
(
&
buffer
,
parse_file
);
}
// see: ngx_conf_parse
int
SrsConfDirective
::
parse_conf
(
SrsFileBuffer
*
buffer
,
SrsDirectiveType
type
)
{
int
ret
=
ERROR_SUCCESS
;
while
(
true
)
{
std
::
vector
<
std
::
string
>
args
;
ret
=
read_token
(
buffer
,
args
);
/**
* ret maybe:
* ERROR_SYSTEM_CONFIG_INVALID error.
* ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
* ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
* ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
* ERROR_SYSTEM_CONFIG_EOF the config file is done
*/
if
(
ret
==
ERROR_SYSTEM_CONFIG_INVALID
)
{
return
ret
;
}
if
(
ret
==
ERROR_SYSTEM_CONFIG_BLOCK_END
)
{
if
(
type
!=
parse_block
)
{
srs_error
(
"line %d: unexpected
\"
}
\"
"
,
buffer
->
line
);
return
ret
;
}
return
ERROR_SUCCESS
;
}
if
(
ret
==
ERROR_SYSTEM_CONFIG_EOF
)
{
if
(
type
==
parse_block
)
{
srs_error
(
"line %d: unexpected end of file, expecting
\"
}
\"
"
,
buffer
->
line
);
return
ret
;
}
return
ERROR_SUCCESS
;
}
if
(
args
.
empty
())
{
srs_error
(
"line %d: empty directive."
,
buffer
->
line
);
return
ret
;
}
// build directive tree.
SrsConfDirective
*
directive
=
new
SrsConfDirective
();
directive
->
conf_line
=
buffer
->
line
;
directive
->
name
=
args
[
0
];
args
.
erase
(
args
.
begin
());
directive
->
args
.
swap
(
args
);
directives
.
push_back
(
directive
);
if
(
ret
==
ERROR_SYSTEM_CONFIG_BLOCK_START
)
{
if
((
ret
=
directive
->
parse_conf
(
buffer
,
parse_block
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
}
return
ret
;
}
// see: ngx_conf_read_token
int
SrsConfDirective
::
read_token
(
SrsFileBuffer
*
buffer
,
std
::
vector
<
std
::
string
>&
args
)
{
int
ret
=
ERROR_SUCCESS
;
char
*
pstart
=
buffer
->
pos
;
int
startline
=
buffer
->
line
;
bool
sharp_comment
=
false
;
bool
d_quoted
=
false
;
bool
s_quoted
=
false
;
bool
need_space
=
false
;
bool
last_space
=
true
;
while
(
true
)
{
if
((
ret
=
refill_buffer
(
buffer
,
d_quoted
,
s_quoted
,
startline
,
pstart
))
!=
ERROR_SUCCESS
)
{
if
(
!
args
.
empty
()
||
!
last_space
)
{
srs_error
(
"line %d: unexpected end of file, expecting ; or
\"
}
\"
"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ret
;
}
char
ch
=
*
buffer
->
pos
++
;
if
(
ch
==
LF
)
{
buffer
->
line
++
;
sharp_comment
=
false
;
}
if
(
sharp_comment
)
{
continue
;
}
if
(
need_space
)
{
if
(
is_common_space
(
ch
))
{
last_space
=
true
;
need_space
=
false
;
continue
;
}
if
(
ch
==
';'
)
{
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
}
if
(
ch
==
'{'
)
{
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
}
srs_error
(
"line %d: unexpected '%c'"
,
buffer
->
line
,
ch
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
// last charecter is space.
if
(
last_space
)
{
if
(
is_common_space
(
ch
))
{
continue
;
}
pstart
=
buffer
->
pos
-
1
;
startline
=
buffer
->
line
;
switch
(
ch
)
{
case
';'
:
if
(
args
.
size
()
==
0
)
{
srs_error
(
"line %d: unexpected ';'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
case
'{'
:
if
(
args
.
size
()
==
0
)
{
srs_error
(
"line %d: unexpected '{'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
case
'}'
:
if
(
args
.
size
()
!=
0
)
{
srs_error
(
"line %d: unexpected '}'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_BLOCK_END
;
case
'#'
:
sharp_comment
=
1
;
continue
;
case
'"'
:
pstart
++
;
d_quoted
=
true
;
last_space
=
0
;
continue
;
case
'\''
:
pstart
++
;
s_quoted
=
true
;
last_space
=
0
;
continue
;
default
:
last_space
=
0
;
continue
;
}
}
else
{
// last charecter is not space
bool
found
=
false
;
if
(
d_quoted
)
{
if
(
ch
==
'"'
)
{
d_quoted
=
false
;
need_space
=
true
;
found
=
true
;
}
}
else
if
(
s_quoted
)
{
if
(
ch
==
'\''
)
{
s_quoted
=
false
;
need_space
=
true
;
found
=
true
;
}
}
else
if
(
is_common_space
(
ch
)
||
ch
==
';'
||
ch
==
'{'
)
{
last_space
=
true
;
found
=
1
;
}
if
(
found
)
{
int
len
=
buffer
->
pos
-
pstart
;
char
*
word
=
new
char
[
len
];
memcpy
(
word
,
pstart
,
len
);
word
[
len
-
1
]
=
0
;
std
::
string
word_str
=
word
;
if
(
!
word_str
.
empty
())
{
args
.
push_back
(
word_str
);
}
srs_freepa
(
word
);
if
(
ch
==
';'
)
{
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
}
if
(
ch
==
'{'
)
{
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
}
}
}
}
return
ret
;
}
int
SrsConfDirective
::
refill_buffer
(
SrsFileBuffer
*
buffer
,
bool
d_quoted
,
bool
s_quoted
,
int
startline
,
char
*&
pstart
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
buffer
->
pos
<
buffer
->
last
)
{
return
ret
;
}
int
size
=
FILE_SIZE
(
buffer
->
fd
)
-
FILE_OFFSET
(
buffer
->
fd
);
if
(
size
>
CONF_BUFFER_SIZE
)
{
ret
=
ERROR_SYSTEM_CONFIG_TOO_LARGE
;
srs_error
(
"config file too large, max=%d, actual=%d, ret=%d"
,
CONF_BUFFER_SIZE
,
size
,
ret
);
return
ret
;
}
if
(
size
<=
0
)
{
return
ERROR_SYSTEM_CONFIG_EOF
;
}
int
len
=
buffer
->
pos
-
buffer
->
start
;
if
(
len
>=
CONF_BUFFER_SIZE
)
{
buffer
->
line
=
startline
;
if
(
!
d_quoted
&&
!
s_quoted
)
{
srs_error
(
"line %d: too long parameter
\"
%*s...
\"
started"
,
buffer
->
line
,
10
,
buffer
->
start
);
}
else
{
srs_error
(
"line %d: too long parameter, "
"probably missing terminating '%c' character"
,
buffer
->
line
,
d_quoted
?
'"'
:
'\''
);
}
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
if
(
len
)
{
memmove
(
buffer
->
start
,
pstart
,
len
);
}
size
=
srs_min
(
size
,
buffer
->
end
-
(
buffer
->
start
+
len
));
int
n
=
read
(
buffer
->
fd
,
buffer
->
start
+
len
,
size
);
if
(
n
!=
size
)
{
srs_error
(
"read file read error. expect %d, actual %d bytes."
,
size
,
n
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
buffer
->
pos
=
buffer
->
start
+
len
;
buffer
->
last
=
buffer
->
pos
+
n
;
pstart
=
buffer
->
start
;
return
ret
;
}
SrsConfig
*
config
=
new
SrsConfig
();
SrsConfig
::
SrsConfig
()
{
show_help
=
false
;
show_version
=
false
;
root
=
new
SrsConfDirective
();
root
->
conf_line
=
0
;
root
->
name
=
"root"
;
}
SrsConfig
::~
SrsConfig
()
{
srs_freep
(
root
);
}
int
SrsConfig
::
reload
()
{
int
ret
=
ERROR_SUCCESS
;
SrsConfig
conf
;
if
((
ret
=
conf
.
parse_file
(
config_file
.
c_str
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"config reloader parse file failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"config reloader parse file success."
);
// store current root to old_root,
// and reap the root from conf to current root.
SrsConfDirective
*
old_root
=
root
;
SrsAutoFree
(
SrsConfDirective
,
old_root
,
false
);
root
=
conf
.
root
;
conf
.
root
=
NULL
;
// merge config.
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
// merge config: listen
if
(
!
srs_directive_equals
(
root
->
get
(
"listen"
),
old_root
->
get
(
"listen"
)))
{
for
(
it
=
subscribes
.
begin
();
it
!=
subscribes
.
end
();
++
it
)
{
SrsReloadHandler
*
subscribe
=
*
it
;
if
((
ret
=
subscribe
->
on_reload_listen
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"notify subscribes reload listen failed. ret=%d"
,
ret
);
return
ret
;
}
}
srs_trace
(
"reload listen success."
);
}
// merge config: pithy_print
if
(
!
srs_directive_equals
(
root
->
get
(
"pithy_print"
),
old_root
->
get
(
"pithy_print"
)))
{
for
(
it
=
subscribes
.
begin
();
it
!=
subscribes
.
end
();
++
it
)
{
SrsReloadHandler
*
subscribe
=
*
it
;
if
((
ret
=
subscribe
->
on_reload_pithy_print
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"notify subscribes pithy_print listen failed. ret=%d"
,
ret
);
return
ret
;
}
}
srs_trace
(
"reload pithy_print success."
);
}
// TODO: suppor reload hls/forward/ffmpeg/http
return
ret
;
}
void
SrsConfig
::
subscribe
(
SrsReloadHandler
*
handler
)
{
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
it
=
std
::
find
(
subscribes
.
begin
(),
subscribes
.
end
(),
handler
);
if
(
it
!=
subscribes
.
end
())
{
return
;
}
subscribes
.
push_back
(
handler
);
}
void
SrsConfig
::
unsubscribe
(
SrsReloadHandler
*
handler
)
{
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
it
=
std
::
find
(
subscribes
.
begin
(),
subscribes
.
end
(),
handler
);
if
(
it
==
subscribes
.
end
())
{
return
;
}
subscribes
.
erase
(
it
);
}
// see: ngx_get_options
int
SrsConfig
::
parse_options
(
int
argc
,
char
**
argv
)
{
int
ret
=
ERROR_SUCCESS
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
{
if
((
ret
=
parse_argv
(
i
,
argv
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
if
(
show_help
)
{
print_help
(
argv
);
}
if
(
show_version
)
{
printf
(
"%s
\n
"
,
RTMP_SIG_SRS_VERSION
);
}
if
(
show_help
||
show_version
)
{
exit
(
0
);
}
if
(
config_file
.
empty
())
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"config file not specified, see help: %s -h, ret=%d"
,
argv
[
0
],
ret
);
return
ret
;
}
return
parse_file
(
config_file
.
c_str
());
}
SrsConfDirective
*
SrsConfig
::
get_vhost
(
std
::
string
vhost
)
{
srs_assert
(
root
);
for
(
int
i
=
0
;
i
<
(
int
)
root
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
conf
=
root
->
at
(
i
);
if
(
conf
->
name
!=
"vhost"
)
{
continue
;
}
if
(
conf
->
arg0
()
==
vhost
)
{
return
conf
;
}
}
if
(
vhost
!=
RTMP_VHOST_DEFAULT
)
{
return
get_vhost
(
RTMP_VHOST_DEFAULT
);
}
return
NULL
;
}
SrsConfDirective
*
SrsConfig
::
get_vhost_on_connect
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
conf
=
conf
->
get
(
"http_hooks"
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"on_connect"
);
}
bool
SrsConfig
::
get_vhost_enabled
(
std
::
string
vhost
)
{
SrsConfDirective
*
vhost_conf
=
get_vhost
(
vhost
);
if
(
!
vhost_conf
)
{
return
true
;
}
SrsConfDirective
*
conf
=
vhost_conf
->
get
(
"enabled"
);
if
(
!
conf
)
{
return
true
;
}
if
(
conf
->
arg0
()
==
"off"
)
{
return
false
;
}
return
true
;
}
SrsConfDirective
*
SrsConfig
::
get_transcode
(
std
::
string
vhost
,
std
::
string
scope
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
SrsConfDirective
*
transcode
=
conf
->
get
(
"transcode"
);
if
(
!
transcode
)
{
return
NULL
;
}
if
(
transcode
->
arg0
()
==
scope
)
{
return
transcode
;
}
return
NULL
;
}
bool
SrsConfig
::
get_transcode_enabled
(
SrsConfDirective
*
transcode
)
{
if
(
!
transcode
)
{
return
false
;
}
SrsConfDirective
*
conf
=
transcode
->
get
(
"enabled"
);
if
(
!
conf
||
conf
->
arg0
()
!=
"on"
)
{
return
false
;
}
return
true
;
}
std
::
string
SrsConfig
::
get_transcode_ffmpeg
(
SrsConfDirective
*
transcode
)
{
if
(
!
transcode
)
{
return
""
;
}
SrsConfDirective
*
conf
=
transcode
->
get
(
"ffmpeg"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
void
SrsConfig
::
get_transcode_engines
(
SrsConfDirective
*
transcode
,
std
::
vector
<
SrsConfDirective
*>&
engines
)
{
if
(
!
transcode
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
transcode
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
conf
=
transcode
->
directives
[
i
];
if
(
conf
->
name
==
"engine"
)
{
engines
.
push_back
(
conf
);
}
}
return
;
}
bool
SrsConfig
::
get_engine_enabled
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
false
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"enabled"
);
if
(
!
conf
||
conf
->
arg0
()
!=
"on"
)
{
return
false
;
}
return
true
;
}
std
::
string
SrsConfig
::
get_engine_vcodec
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vcodec"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_engine_vbitrate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vbitrate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
double
SrsConfig
::
get_engine_vfps
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vfps"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atof
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vwidth
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vwidth"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vheight
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vheight"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vthreads
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vthreads"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
std
::
string
SrsConfig
::
get_engine_vprofile
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vprofile"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
std
::
string
SrsConfig
::
get_engine_vpreset
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vpreset"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
void
SrsConfig
::
get_engine_vparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vparams
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vparams"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
vparams
.
push_back
(
"-"
+
p
->
name
);
vparams
.
push_back
(
p
->
arg0
());
}
}
void
SrsConfig
::
get_engine_vfilter
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vfilter
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vfilter"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
vfilter
.
push_back
(
"-"
+
p
->
name
);
vfilter
.
push_back
(
p
->
arg0
());
}
}
std
::
string
SrsConfig
::
get_engine_acodec
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"acodec"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_engine_abitrate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"abitrate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_asample_rate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"asample_rate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_achannels
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"achannels"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
void
SrsConfig
::
get_engine_aparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
aparams
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"aparams"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
aparams
.
push_back
(
"-"
+
p
->
name
);
aparams
.
push_back
(
p
->
arg0
());
}
}
std
::
string
SrsConfig
::
get_engine_output
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"output"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
std
::
string
SrsConfig
::
get_log_dir
()
{
srs_assert
(
root
);
SrsConfDirective
*
conf
=
root
->
get
(
"log_dir"
);
if
(
!
conf
||
conf
->
arg0
().
empty
())
{
return
"./objs/logs"
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_max_connections
()
{
srs_assert
(
root
);
SrsConfDirective
*
conf
=
root
->
get
(
"max_connections"
);
if
(
!
conf
||
conf
->
arg0
().
empty
())
{
return
2000
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
SrsConfDirective
*
SrsConfig
::
get_gop_cache
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"gop_cache"
);
}
SrsConfDirective
*
SrsConfig
::
get_forward
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"forward"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls"
);
}
bool
SrsConfig
::
get_hls_enabled
(
std
::
string
vhost
)
{
SrsConfDirective
*
hls
=
get_hls
(
vhost
);
if
(
!
hls
)
{
return
true
;
}
if
(
hls
->
arg0
()
==
"off"
)
{
return
false
;
}
return
true
;
}
SrsConfDirective
*
SrsConfig
::
get_hls_path
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_path"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls_fragment
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_fragment"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls_window
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_window"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer_play
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer_play"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer_publish
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer_publish"
);
}
SrsConfDirective
*
SrsConfig
::
get_listen
()
{
return
root
->
get
(
"listen"
);
}
SrsConfDirective
*
SrsConfig
::
get_chunk_size
()
{
return
root
->
get
(
"chunk_size"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_publish
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"publish"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_forwarder
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"forwarder"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_hls
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"hls"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_encoder
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"encoder"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"forwarder"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_play
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"play"
);
}
int
SrsConfig
::
parse_file
(
const
char
*
filename
)
{
int
ret
=
ERROR_SUCCESS
;
config_file
=
filename
;
if
(
config_file
.
empty
())
{
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
if
((
ret
=
root
->
parse
(
config_file
.
c_str
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
SrsConfDirective
*
conf
=
NULL
;
if
((
conf
=
get_listen
())
==
NULL
||
conf
->
args
.
size
()
==
0
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"line %d: conf error, "
"directive
\"
listen
\"
is empty, ret=%d"
,
(
conf
?
conf
->
conf_line
:
0
),
ret
);
return
ret
;
}
// TODO: check the hls.
// TODO: check other config.
// TODO: check hls.
// TODO: check ssl.
// TODO: check ffmpeg.
// TODO: check http.
return
ret
;
}
int
SrsConfig
::
parse_argv
(
int
&
i
,
char
**
argv
)
{
int
ret
=
ERROR_SUCCESS
;
char
*
p
=
argv
[
i
];
if
(
*
p
++
!=
'-'
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"invalid options(index=%d, value=%s), "
"must starts with -, see help: %s -h, ret=%d"
,
i
,
argv
[
i
],
argv
[
0
],
ret
);
return
ret
;
}
while
(
*
p
)
{
switch
(
*
p
++
)
{
case
'?'
:
case
'h'
:
show_help
=
true
;
break
;
case
'v'
:
case
'V'
:
show_version
=
true
;
break
;
case
'c'
:
if
(
*
p
)
{
config_file
=
p
;
return
ret
;
}
if
(
argv
[
++
i
])
{
config_file
=
argv
[
i
];
return
ret
;
}
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"option
\"
-c
\"
requires parameter, ret=%d"
,
ret
);
return
ret
;
default
:
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"invalid option:
\"
%c
\"
, see help: %s -h, ret=%d"
,
*
(
p
-
1
),
argv
[
0
],
ret
);
return
ret
;
}
}
return
ret
;
}
void
SrsConfig
::
print_help
(
char
**
argv
)
{
printf
(
RTMP_SIG_SRS_NAME
" "
RTMP_SIG_SRS_VERSION
" Copyright (c) 2013 winlin
\n
"
"Contributors: "
RTMP_SIG_SRS_CONTRIBUTOR
"
\n
"
"Build: "
SRS_BUILD_DATE
" Configuration: "
SRS_CONFIGURE
"
\n
"
"Usage: %s [-h?vV] [-c <filename>]
\n
"
"
\n
"
"Options:
\n
"
" -?-h : show help
\n
"
" -v-V : show version and exit
\n
"
" -c filename : set configuration file
\n
"
"
\n
"
RTMP_SIG_SRS_WEB
"
\n
"
RTMP_SIG_SRS_URL
"
\n
"
"Email: "
RTMP_SIG_SRS_EMAIL
"
\n
"
"
\n
"
,
argv
[
0
]);
}
bool
srs_directive_equals
(
SrsConfDirective
*
a
,
SrsConfDirective
*
b
)
{
if
(
!
a
||
!
b
)
{
return
false
;
}
if
(
a
->
name
!=
b
->
name
)
{
return
false
;
}
if
(
a
->
args
.
size
()
!=
b
->
args
.
size
())
{
return
false
;
}
for
(
int
i
=
0
;
i
<
(
int
)
a
->
args
.
size
();
i
++
)
{
if
(
a
->
args
.
at
(
i
)
!=
b
->
args
.
at
(
i
))
{
return
false
;
}
}
if
(
a
->
directives
.
size
()
!=
b
->
directives
.
size
())
{
return
false
;
}
for
(
int
i
=
0
;
i
<
(
int
)
a
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
a0
=
a
->
at
(
i
);
SrsConfDirective
*
b0
=
b
->
at
(
i
);
if
(
!
srs_directive_equals
(
a0
,
b0
))
{
return
false
;
}
}
return
true
;
}
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_config.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// file operations.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_autofree.hpp>
#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
int64_t
FILE_SIZE
(
int
fd
)
{
int64_t
pre
=
FILE_OFFSET
(
fd
);
int64_t
pos
=
lseek
(
fd
,
0
,
SEEK_END
);
lseek
(
fd
,
pre
,
SEEK_SET
);
return
pos
;
}
#define LF (char)0x0a
#define CR (char)0x0d
bool
is_common_space
(
char
ch
)
{
return
(
ch
==
' '
||
ch
==
'\t'
||
ch
==
CR
||
ch
==
LF
);
}
#define CONF_BUFFER_SIZE 1024 * 1024
SrsFileBuffer
::
SrsFileBuffer
()
{
fd
=
-
1
;
line
=
0
;
pos
=
last
=
start
=
new
char
[
CONF_BUFFER_SIZE
];
end
=
start
+
CONF_BUFFER_SIZE
;
}
SrsFileBuffer
::~
SrsFileBuffer
()
{
if
(
fd
>
0
)
{
close
(
fd
);
}
srs_freepa
(
start
);
}
int
SrsFileBuffer
::
open
(
const
char
*
filename
)
{
assert
(
fd
==
-
1
);
if
((
fd
=
::
open
(
filename
,
O_RDONLY
,
0
))
<
0
)
{
srs_error
(
"open conf file error. errno=%d(%s)"
,
errno
,
strerror
(
errno
));
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
line
=
1
;
return
ERROR_SUCCESS
;
}
SrsConfDirective
::
SrsConfDirective
()
{
}
SrsConfDirective
::~
SrsConfDirective
()
{
std
::
vector
<
SrsConfDirective
*>::
iterator
it
;
for
(
it
=
directives
.
begin
();
it
!=
directives
.
end
();
++
it
)
{
SrsConfDirective
*
directive
=
*
it
;
srs_freep
(
directive
);
}
directives
.
clear
();
}
std
::
string
SrsConfDirective
::
arg0
()
{
if
(
args
.
size
()
>
0
)
{
return
args
.
at
(
0
);
}
return
""
;
}
std
::
string
SrsConfDirective
::
arg1
()
{
if
(
args
.
size
()
>
1
)
{
return
args
.
at
(
1
);
}
return
""
;
}
std
::
string
SrsConfDirective
::
arg2
()
{
if
(
args
.
size
()
>
2
)
{
return
args
.
at
(
2
);
}
return
""
;
}
SrsConfDirective
*
SrsConfDirective
::
at
(
int
index
)
{
return
directives
.
at
(
index
);
}
SrsConfDirective
*
SrsConfDirective
::
get
(
std
::
string
_name
)
{
std
::
vector
<
SrsConfDirective
*>::
iterator
it
;
for
(
it
=
directives
.
begin
();
it
!=
directives
.
end
();
++
it
)
{
SrsConfDirective
*
directive
=
*
it
;
if
(
directive
->
name
==
_name
)
{
return
directive
;
}
}
return
NULL
;
}
int
SrsConfDirective
::
parse
(
const
char
*
filename
)
{
int
ret
=
ERROR_SUCCESS
;
SrsFileBuffer
buffer
;
if
((
ret
=
buffer
.
open
(
filename
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
parse_conf
(
&
buffer
,
parse_file
);
}
// see: ngx_conf_parse
int
SrsConfDirective
::
parse_conf
(
SrsFileBuffer
*
buffer
,
SrsDirectiveType
type
)
{
int
ret
=
ERROR_SUCCESS
;
while
(
true
)
{
std
::
vector
<
std
::
string
>
args
;
ret
=
read_token
(
buffer
,
args
);
/**
* ret maybe:
* ERROR_SYSTEM_CONFIG_INVALID error.
* ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
* ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
* ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
* ERROR_SYSTEM_CONFIG_EOF the config file is done
*/
if
(
ret
==
ERROR_SYSTEM_CONFIG_INVALID
)
{
return
ret
;
}
if
(
ret
==
ERROR_SYSTEM_CONFIG_BLOCK_END
)
{
if
(
type
!=
parse_block
)
{
srs_error
(
"line %d: unexpected
\"
}
\"
"
,
buffer
->
line
);
return
ret
;
}
return
ERROR_SUCCESS
;
}
if
(
ret
==
ERROR_SYSTEM_CONFIG_EOF
)
{
if
(
type
==
parse_block
)
{
srs_error
(
"line %d: unexpected end of file, expecting
\"
}
\"
"
,
buffer
->
line
);
return
ret
;
}
return
ERROR_SUCCESS
;
}
if
(
args
.
empty
())
{
srs_error
(
"line %d: empty directive."
,
buffer
->
line
);
return
ret
;
}
// build directive tree.
SrsConfDirective
*
directive
=
new
SrsConfDirective
();
directive
->
conf_line
=
buffer
->
line
;
directive
->
name
=
args
[
0
];
args
.
erase
(
args
.
begin
());
directive
->
args
.
swap
(
args
);
directives
.
push_back
(
directive
);
if
(
ret
==
ERROR_SYSTEM_CONFIG_BLOCK_START
)
{
if
((
ret
=
directive
->
parse_conf
(
buffer
,
parse_block
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
}
return
ret
;
}
// see: ngx_conf_read_token
int
SrsConfDirective
::
read_token
(
SrsFileBuffer
*
buffer
,
std
::
vector
<
std
::
string
>&
args
)
{
int
ret
=
ERROR_SUCCESS
;
char
*
pstart
=
buffer
->
pos
;
int
startline
=
buffer
->
line
;
bool
sharp_comment
=
false
;
bool
d_quoted
=
false
;
bool
s_quoted
=
false
;
bool
need_space
=
false
;
bool
last_space
=
true
;
while
(
true
)
{
if
((
ret
=
refill_buffer
(
buffer
,
d_quoted
,
s_quoted
,
startline
,
pstart
))
!=
ERROR_SUCCESS
)
{
if
(
!
args
.
empty
()
||
!
last_space
)
{
srs_error
(
"line %d: unexpected end of file, expecting ; or
\"
}
\"
"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ret
;
}
char
ch
=
*
buffer
->
pos
++
;
if
(
ch
==
LF
)
{
buffer
->
line
++
;
sharp_comment
=
false
;
}
if
(
sharp_comment
)
{
continue
;
}
if
(
need_space
)
{
if
(
is_common_space
(
ch
))
{
last_space
=
true
;
need_space
=
false
;
continue
;
}
if
(
ch
==
';'
)
{
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
}
if
(
ch
==
'{'
)
{
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
}
srs_error
(
"line %d: unexpected '%c'"
,
buffer
->
line
,
ch
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
// last charecter is space.
if
(
last_space
)
{
if
(
is_common_space
(
ch
))
{
continue
;
}
pstart
=
buffer
->
pos
-
1
;
startline
=
buffer
->
line
;
switch
(
ch
)
{
case
';'
:
if
(
args
.
size
()
==
0
)
{
srs_error
(
"line %d: unexpected ';'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
case
'{'
:
if
(
args
.
size
()
==
0
)
{
srs_error
(
"line %d: unexpected '{'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
case
'}'
:
if
(
args
.
size
()
!=
0
)
{
srs_error
(
"line %d: unexpected '}'"
,
buffer
->
line
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
return
ERROR_SYSTEM_CONFIG_BLOCK_END
;
case
'#'
:
sharp_comment
=
1
;
continue
;
case
'"'
:
pstart
++
;
d_quoted
=
true
;
last_space
=
0
;
continue
;
case
'\''
:
pstart
++
;
s_quoted
=
true
;
last_space
=
0
;
continue
;
default
:
last_space
=
0
;
continue
;
}
}
else
{
// last charecter is not space
bool
found
=
false
;
if
(
d_quoted
)
{
if
(
ch
==
'"'
)
{
d_quoted
=
false
;
need_space
=
true
;
found
=
true
;
}
}
else
if
(
s_quoted
)
{
if
(
ch
==
'\''
)
{
s_quoted
=
false
;
need_space
=
true
;
found
=
true
;
}
}
else
if
(
is_common_space
(
ch
)
||
ch
==
';'
||
ch
==
'{'
)
{
last_space
=
true
;
found
=
1
;
}
if
(
found
)
{
int
len
=
buffer
->
pos
-
pstart
;
char
*
word
=
new
char
[
len
];
memcpy
(
word
,
pstart
,
len
);
word
[
len
-
1
]
=
0
;
std
::
string
word_str
=
word
;
if
(
!
word_str
.
empty
())
{
args
.
push_back
(
word_str
);
}
srs_freepa
(
word
);
if
(
ch
==
';'
)
{
return
ERROR_SYSTEM_CONFIG_DIRECTIVE
;
}
if
(
ch
==
'{'
)
{
return
ERROR_SYSTEM_CONFIG_BLOCK_START
;
}
}
}
}
return
ret
;
}
int
SrsConfDirective
::
refill_buffer
(
SrsFileBuffer
*
buffer
,
bool
d_quoted
,
bool
s_quoted
,
int
startline
,
char
*&
pstart
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
buffer
->
pos
<
buffer
->
last
)
{
return
ret
;
}
int
size
=
FILE_SIZE
(
buffer
->
fd
)
-
FILE_OFFSET
(
buffer
->
fd
);
if
(
size
>
CONF_BUFFER_SIZE
)
{
ret
=
ERROR_SYSTEM_CONFIG_TOO_LARGE
;
srs_error
(
"config file too large, max=%d, actual=%d, ret=%d"
,
CONF_BUFFER_SIZE
,
size
,
ret
);
return
ret
;
}
if
(
size
<=
0
)
{
return
ERROR_SYSTEM_CONFIG_EOF
;
}
int
len
=
buffer
->
pos
-
buffer
->
start
;
if
(
len
>=
CONF_BUFFER_SIZE
)
{
buffer
->
line
=
startline
;
if
(
!
d_quoted
&&
!
s_quoted
)
{
srs_error
(
"line %d: too long parameter
\"
%*s...
\"
started"
,
buffer
->
line
,
10
,
buffer
->
start
);
}
else
{
srs_error
(
"line %d: too long parameter, "
"probably missing terminating '%c' character"
,
buffer
->
line
,
d_quoted
?
'"'
:
'\''
);
}
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
if
(
len
)
{
memmove
(
buffer
->
start
,
pstart
,
len
);
}
size
=
srs_min
(
size
,
buffer
->
end
-
(
buffer
->
start
+
len
));
int
n
=
read
(
buffer
->
fd
,
buffer
->
start
+
len
,
size
);
if
(
n
!=
size
)
{
srs_error
(
"read file read error. expect %d, actual %d bytes."
,
size
,
n
);
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
buffer
->
pos
=
buffer
->
start
+
len
;
buffer
->
last
=
buffer
->
pos
+
n
;
pstart
=
buffer
->
start
;
return
ret
;
}
SrsConfig
*
config
=
new
SrsConfig
();
SrsConfig
::
SrsConfig
()
{
show_help
=
false
;
show_version
=
false
;
root
=
new
SrsConfDirective
();
root
->
conf_line
=
0
;
root
->
name
=
"root"
;
}
SrsConfig
::~
SrsConfig
()
{
srs_freep
(
root
);
}
int
SrsConfig
::
reload
()
{
int
ret
=
ERROR_SUCCESS
;
SrsConfig
conf
;
if
((
ret
=
conf
.
parse_file
(
config_file
.
c_str
()))
!=
ERROR_SUCCESS
)
{
srs_error
(
"config reloader parse file failed. ret=%d"
,
ret
);
return
ret
;
}
srs_info
(
"config reloader parse file success."
);
// store current root to old_root,
// and reap the root from conf to current root.
SrsConfDirective
*
old_root
=
root
;
SrsAutoFree
(
SrsConfDirective
,
old_root
,
false
);
root
=
conf
.
root
;
conf
.
root
=
NULL
;
// merge config.
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
// merge config: listen
if
(
!
srs_directive_equals
(
root
->
get
(
"listen"
),
old_root
->
get
(
"listen"
)))
{
for
(
it
=
subscribes
.
begin
();
it
!=
subscribes
.
end
();
++
it
)
{
SrsReloadHandler
*
subscribe
=
*
it
;
if
((
ret
=
subscribe
->
on_reload_listen
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"notify subscribes reload listen failed. ret=%d"
,
ret
);
return
ret
;
}
}
srs_trace
(
"reload listen success."
);
}
// merge config: pithy_print
if
(
!
srs_directive_equals
(
root
->
get
(
"pithy_print"
),
old_root
->
get
(
"pithy_print"
)))
{
for
(
it
=
subscribes
.
begin
();
it
!=
subscribes
.
end
();
++
it
)
{
SrsReloadHandler
*
subscribe
=
*
it
;
if
((
ret
=
subscribe
->
on_reload_pithy_print
())
!=
ERROR_SUCCESS
)
{
srs_error
(
"notify subscribes pithy_print listen failed. ret=%d"
,
ret
);
return
ret
;
}
}
srs_trace
(
"reload pithy_print success."
);
}
// TODO: suppor reload hls/forward/ffmpeg/http
return
ret
;
}
void
SrsConfig
::
subscribe
(
SrsReloadHandler
*
handler
)
{
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
it
=
std
::
find
(
subscribes
.
begin
(),
subscribes
.
end
(),
handler
);
if
(
it
!=
subscribes
.
end
())
{
return
;
}
subscribes
.
push_back
(
handler
);
}
void
SrsConfig
::
unsubscribe
(
SrsReloadHandler
*
handler
)
{
std
::
vector
<
SrsReloadHandler
*>::
iterator
it
;
it
=
std
::
find
(
subscribes
.
begin
(),
subscribes
.
end
(),
handler
);
if
(
it
==
subscribes
.
end
())
{
return
;
}
subscribes
.
erase
(
it
);
}
// see: ngx_get_options
int
SrsConfig
::
parse_options
(
int
argc
,
char
**
argv
)
{
int
ret
=
ERROR_SUCCESS
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
{
if
((
ret
=
parse_argv
(
i
,
argv
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
}
if
(
show_help
)
{
print_help
(
argv
);
}
if
(
show_version
)
{
printf
(
"%s
\n
"
,
RTMP_SIG_SRS_VERSION
);
}
if
(
show_help
||
show_version
)
{
exit
(
0
);
}
if
(
config_file
.
empty
())
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"config file not specified, see help: %s -h, ret=%d"
,
argv
[
0
],
ret
);
return
ret
;
}
return
parse_file
(
config_file
.
c_str
());
}
SrsConfDirective
*
SrsConfig
::
get_vhost
(
std
::
string
vhost
)
{
srs_assert
(
root
);
for
(
int
i
=
0
;
i
<
(
int
)
root
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
conf
=
root
->
at
(
i
);
if
(
conf
->
name
!=
"vhost"
)
{
continue
;
}
if
(
conf
->
arg0
()
==
vhost
)
{
return
conf
;
}
}
if
(
vhost
!=
RTMP_VHOST_DEFAULT
)
{
return
get_vhost
(
RTMP_VHOST_DEFAULT
);
}
return
NULL
;
}
SrsConfDirective
*
SrsConfig
::
get_vhost_on_connect
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
conf
=
conf
->
get
(
"http_hooks"
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"on_connect"
);
}
bool
SrsConfig
::
get_vhost_enabled
(
std
::
string
vhost
)
{
SrsConfDirective
*
vhost_conf
=
get_vhost
(
vhost
);
if
(
!
vhost_conf
)
{
return
true
;
}
SrsConfDirective
*
conf
=
vhost_conf
->
get
(
"enabled"
);
if
(
!
conf
)
{
return
true
;
}
if
(
conf
->
arg0
()
==
"off"
)
{
return
false
;
}
return
true
;
}
SrsConfDirective
*
SrsConfig
::
get_transcode
(
std
::
string
vhost
,
std
::
string
scope
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
SrsConfDirective
*
transcode
=
conf
->
get
(
"transcode"
);
if
(
!
transcode
)
{
return
NULL
;
}
if
(
transcode
->
arg0
()
==
scope
)
{
return
transcode
;
}
return
NULL
;
}
bool
SrsConfig
::
get_transcode_enabled
(
SrsConfDirective
*
transcode
)
{
if
(
!
transcode
)
{
return
false
;
}
SrsConfDirective
*
conf
=
transcode
->
get
(
"enabled"
);
if
(
!
conf
||
conf
->
arg0
()
!=
"on"
)
{
return
false
;
}
return
true
;
}
std
::
string
SrsConfig
::
get_transcode_ffmpeg
(
SrsConfDirective
*
transcode
)
{
if
(
!
transcode
)
{
return
""
;
}
SrsConfDirective
*
conf
=
transcode
->
get
(
"ffmpeg"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
void
SrsConfig
::
get_transcode_engines
(
SrsConfDirective
*
transcode
,
std
::
vector
<
SrsConfDirective
*>&
engines
)
{
if
(
!
transcode
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
transcode
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
conf
=
transcode
->
directives
[
i
];
if
(
conf
->
name
==
"engine"
)
{
engines
.
push_back
(
conf
);
}
}
return
;
}
bool
SrsConfig
::
get_engine_enabled
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
false
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"enabled"
);
if
(
!
conf
||
conf
->
arg0
()
!=
"on"
)
{
return
false
;
}
return
true
;
}
std
::
string
SrsConfig
::
get_engine_vcodec
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vcodec"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_engine_vbitrate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vbitrate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
double
SrsConfig
::
get_engine_vfps
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vfps"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atof
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vwidth
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vwidth"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vheight
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vheight"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_vthreads
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vthreads"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
std
::
string
SrsConfig
::
get_engine_vprofile
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vprofile"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
std
::
string
SrsConfig
::
get_engine_vpreset
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vpreset"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
void
SrsConfig
::
get_engine_vparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vparams
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vparams"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
vparams
.
push_back
(
"-"
+
p
->
name
);
vparams
.
push_back
(
p
->
arg0
());
}
}
void
SrsConfig
::
get_engine_vfilter
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vfilter
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"vfilter"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
vfilter
.
push_back
(
"-"
+
p
->
name
);
vfilter
.
push_back
(
p
->
arg0
());
}
}
std
::
string
SrsConfig
::
get_engine_acodec
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"acodec"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_engine_abitrate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"abitrate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_asample_rate
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"asample_rate"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
int
SrsConfig
::
get_engine_achannels
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
0
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"achannels"
);
if
(
!
conf
)
{
return
0
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
void
SrsConfig
::
get_engine_aparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
aparams
)
{
if
(
!
engine
)
{
return
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"aparams"
);
if
(
!
conf
)
{
return
;
}
for
(
int
i
=
0
;
i
<
(
int
)
conf
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
p
=
conf
->
directives
[
i
];
if
(
!
p
)
{
continue
;
}
aparams
.
push_back
(
"-"
+
p
->
name
);
aparams
.
push_back
(
p
->
arg0
());
}
}
std
::
string
SrsConfig
::
get_engine_output
(
SrsConfDirective
*
engine
)
{
if
(
!
engine
)
{
return
""
;
}
SrsConfDirective
*
conf
=
engine
->
get
(
"output"
);
if
(
!
conf
)
{
return
""
;
}
return
conf
->
arg0
();
}
std
::
string
SrsConfig
::
get_log_dir
()
{
srs_assert
(
root
);
SrsConfDirective
*
conf
=
root
->
get
(
"log_dir"
);
if
(
!
conf
||
conf
->
arg0
().
empty
())
{
return
"./objs/logs"
;
}
return
conf
->
arg0
();
}
int
SrsConfig
::
get_max_connections
()
{
srs_assert
(
root
);
SrsConfDirective
*
conf
=
root
->
get
(
"max_connections"
);
if
(
!
conf
||
conf
->
arg0
().
empty
())
{
return
2000
;
}
return
::
atoi
(
conf
->
arg0
().
c_str
());
}
SrsConfDirective
*
SrsConfig
::
get_gop_cache
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"gop_cache"
);
}
SrsConfDirective
*
SrsConfig
::
get_forward
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"forward"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls"
);
}
bool
SrsConfig
::
get_hls_enabled
(
std
::
string
vhost
)
{
SrsConfDirective
*
hls
=
get_hls
(
vhost
);
if
(
!
hls
)
{
return
true
;
}
if
(
hls
->
arg0
()
==
"off"
)
{
return
false
;
}
return
true
;
}
SrsConfDirective
*
SrsConfig
::
get_hls_path
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_path"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls_fragment
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_fragment"
);
}
SrsConfDirective
*
SrsConfig
::
get_hls_window
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"hls_window"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer_play
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer_play"
);
}
SrsConfDirective
*
SrsConfig
::
get_refer_publish
(
std
::
string
vhost
)
{
SrsConfDirective
*
conf
=
get_vhost
(
vhost
);
if
(
!
conf
)
{
return
NULL
;
}
return
conf
->
get
(
"refer_publish"
);
}
SrsConfDirective
*
SrsConfig
::
get_listen
()
{
return
root
->
get
(
"listen"
);
}
SrsConfDirective
*
SrsConfig
::
get_chunk_size
()
{
return
root
->
get
(
"chunk_size"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_publish
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"publish"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_forwarder
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"forwarder"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_hls
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"hls"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_encoder
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"encoder"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"forwarder"
);
}
SrsConfDirective
*
SrsConfig
::
get_pithy_print_play
()
{
SrsConfDirective
*
pithy
=
root
->
get
(
"pithy_print"
);
if
(
!
pithy
)
{
return
NULL
;
}
return
pithy
->
get
(
"play"
);
}
int
SrsConfig
::
parse_file
(
const
char
*
filename
)
{
int
ret
=
ERROR_SUCCESS
;
config_file
=
filename
;
if
(
config_file
.
empty
())
{
return
ERROR_SYSTEM_CONFIG_INVALID
;
}
if
((
ret
=
root
->
parse
(
config_file
.
c_str
()))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
SrsConfDirective
*
conf
=
NULL
;
if
((
conf
=
get_listen
())
==
NULL
||
conf
->
args
.
size
()
==
0
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"line %d: conf error, "
"directive
\"
listen
\"
is empty, ret=%d"
,
(
conf
?
conf
->
conf_line
:
0
),
ret
);
return
ret
;
}
// TODO: check the hls.
// TODO: check other config.
// TODO: check hls.
// TODO: check ssl.
// TODO: check ffmpeg.
// TODO: check http.
return
ret
;
}
int
SrsConfig
::
parse_argv
(
int
&
i
,
char
**
argv
)
{
int
ret
=
ERROR_SUCCESS
;
char
*
p
=
argv
[
i
];
if
(
*
p
++
!=
'-'
)
{
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"invalid options(index=%d, value=%s), "
"must starts with -, see help: %s -h, ret=%d"
,
i
,
argv
[
i
],
argv
[
0
],
ret
);
return
ret
;
}
while
(
*
p
)
{
switch
(
*
p
++
)
{
case
'?'
:
case
'h'
:
show_help
=
true
;
break
;
case
'v'
:
case
'V'
:
show_version
=
true
;
break
;
case
'c'
:
if
(
*
p
)
{
config_file
=
p
;
return
ret
;
}
if
(
argv
[
++
i
])
{
config_file
=
argv
[
i
];
return
ret
;
}
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"option
\"
-c
\"
requires parameter, ret=%d"
,
ret
);
return
ret
;
default
:
ret
=
ERROR_SYSTEM_CONFIG_INVALID
;
srs_error
(
"invalid option:
\"
%c
\"
, see help: %s -h, ret=%d"
,
*
(
p
-
1
),
argv
[
0
],
ret
);
return
ret
;
}
}
return
ret
;
}
void
SrsConfig
::
print_help
(
char
**
argv
)
{
printf
(
RTMP_SIG_SRS_NAME
" "
RTMP_SIG_SRS_VERSION
" Copyright (c) 2013 winlin
\n
"
"Contributors: "
RTMP_SIG_SRS_CONTRIBUTOR
"
\n
"
"Build: "
SRS_BUILD_DATE
" Configuration: "
SRS_CONFIGURE
"
\n
"
"Usage: %s [-h?vV] [-c <filename>]
\n
"
"
\n
"
"Options:
\n
"
" -?-h : show help
\n
"
" -v-V : show version and exit
\n
"
" -c filename : set configuration file
\n
"
"
\n
"
RTMP_SIG_SRS_WEB
"
\n
"
RTMP_SIG_SRS_URL
"
\n
"
"Email: "
RTMP_SIG_SRS_EMAIL
"
\n
"
"
\n
"
,
argv
[
0
]);
}
bool
srs_directive_equals
(
SrsConfDirective
*
a
,
SrsConfDirective
*
b
)
{
if
(
!
a
||
!
b
)
{
return
false
;
}
if
(
a
->
name
!=
b
->
name
)
{
return
false
;
}
if
(
a
->
args
.
size
()
!=
b
->
args
.
size
())
{
return
false
;
}
for
(
int
i
=
0
;
i
<
(
int
)
a
->
args
.
size
();
i
++
)
{
if
(
a
->
args
.
at
(
i
)
!=
b
->
args
.
at
(
i
))
{
return
false
;
}
}
if
(
a
->
directives
.
size
()
!=
b
->
directives
.
size
())
{
return
false
;
}
for
(
int
i
=
0
;
i
<
(
int
)
a
->
directives
.
size
();
i
++
)
{
SrsConfDirective
*
a0
=
a
->
at
(
i
);
SrsConfDirective
*
b0
=
b
->
at
(
i
);
if
(
!
srs_directive_equals
(
a0
,
b0
))
{
return
false
;
}
}
return
true
;
}
...
...
trunk/src/core/srs_core_config.hpp
100755 → 100644
查看文件 @
2887cda
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_CORE_CONIFG_HPP
#define SRS_CORE_CONIFG_HPP
/*
#include <srs_core_config.hpp>
*/
#include <srs_core.hpp>
#include <vector>
#include <string>
#include <srs_core_reload.hpp>
// default vhost for rtmp
#define RTMP_VHOST_DEFAULT "__defaultVhost__"
#define SRS_LOCALHOST "127.0.0.1"
#define RTMP_DEFAULT_PORT 1935
#define RTMP_DEFAULT_PORTS "1935"
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
#define SRS_CONF_DEFAULT_HLS_WINDOW 60
// in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100
// in ms, for HLS aac flush the audio
#define SRS_CONF_DEFAULT_AAC_DELAY 300
class
SrsFileBuffer
{
public
:
int
fd
;
int
line
;
// start of buffer.
char
*
start
;
// end of buffer.
char
*
end
;
// current consumed position.
char
*
pos
;
// last available position.
char
*
last
;
SrsFileBuffer
();
virtual
~
SrsFileBuffer
();
virtual
int
open
(
const
char
*
filename
);
};
class
SrsConfDirective
{
public
:
int
conf_line
;
std
::
string
name
;
std
::
vector
<
std
::
string
>
args
;
std
::
vector
<
SrsConfDirective
*>
directives
;
public
:
SrsConfDirective
();
virtual
~
SrsConfDirective
();
std
::
string
arg0
();
std
::
string
arg1
();
std
::
string
arg2
();
SrsConfDirective
*
at
(
int
index
);
SrsConfDirective
*
get
(
std
::
string
_name
);
public
:
virtual
int
parse
(
const
char
*
filename
);
public
:
enum
SrsDirectiveType
{
parse_file
,
parse_block
};
virtual
int
parse_conf
(
SrsFileBuffer
*
buffer
,
SrsDirectiveType
type
);
virtual
int
read_token
(
SrsFileBuffer
*
buffer
,
std
::
vector
<
std
::
string
>&
args
);
virtual
int
refill_buffer
(
SrsFileBuffer
*
buffer
,
bool
d_quoted
,
bool
s_quoted
,
int
startline
,
char
*&
pstart
);
};
/**
* the config parser.
* for the config supports reload, so never keep the reference cross st-thread,
* that is, never save the SrsConfDirective* get by any api of config,
* for it maybe free in the reload st-thread cycle.
* you can keep it before st-thread switch, or simply never keep it.
*/
class
SrsConfig
{
private
:
bool
show_help
;
bool
show_version
;
std
::
string
config_file
;
SrsConfDirective
*
root
;
std
::
vector
<
SrsReloadHandler
*>
subscribes
;
public
:
SrsConfig
();
virtual
~
SrsConfig
();
public
:
virtual
int
reload
();
virtual
void
subscribe
(
SrsReloadHandler
*
handler
);
virtual
void
unsubscribe
(
SrsReloadHandler
*
handler
);
public
:
virtual
int
parse_options
(
int
argc
,
char
**
argv
);
public
:
virtual
SrsConfDirective
*
get_vhost
(
std
::
string
vhost
);
virtual
bool
get_vhost_enabled
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_vhost_on_connect
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_transcode
(
std
::
string
vhost
,
std
::
string
scope
);
virtual
bool
get_transcode_enabled
(
SrsConfDirective
*
transcode
);
virtual
std
::
string
get_transcode_ffmpeg
(
SrsConfDirective
*
transcode
);
virtual
void
get_transcode_engines
(
SrsConfDirective
*
transcode
,
std
::
vector
<
SrsConfDirective
*>&
engines
);
virtual
bool
get_engine_enabled
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vcodec
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vbitrate
(
SrsConfDirective
*
engine
);
virtual
double
get_engine_vfps
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vwidth
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vheight
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vthreads
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vprofile
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vpreset
(
SrsConfDirective
*
engine
);
virtual
void
get_engine_vparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vparams
);
virtual
void
get_engine_vfilter
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vfilter
);
virtual
std
::
string
get_engine_acodec
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_abitrate
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_asample_rate
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_achannels
(
SrsConfDirective
*
engine
);
virtual
void
get_engine_aparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
aparams
);
virtual
std
::
string
get_engine_output
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_log_dir
();
virtual
int
get_max_connections
();
virtual
SrsConfDirective
*
get_gop_cache
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_forward
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls
(
std
::
string
vhost
);
virtual
bool
get_hls_enabled
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_path
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_fragment
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_window
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer_play
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer_publish
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_listen
();
virtual
SrsConfDirective
*
get_chunk_size
();
virtual
SrsConfDirective
*
get_pithy_print_publish
();
virtual
SrsConfDirective
*
get_pithy_print_forwarder
();
virtual
SrsConfDirective
*
get_pithy_print_encoder
();
virtual
SrsConfDirective
*
get_pithy_print_hls
();
virtual
SrsConfDirective
*
get_pithy_print_play
();
private
:
virtual
int
parse_file
(
const
char
*
filename
);
virtual
int
parse_argv
(
int
&
i
,
char
**
argv
);
virtual
void
print_help
(
char
**
argv
);
};
/**
* deep compare directive.
*/
bool
srs_directive_equals
(
SrsConfDirective
*
a
,
SrsConfDirective
*
b
);
// global config
extern
SrsConfig
*
config
;
/*
The MIT License (MIT)
Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_CORE_CONIFG_HPP
#define SRS_CORE_CONIFG_HPP
/*
#include <srs_core_config.hpp>
*/
#include <srs_core.hpp>
#include <vector>
#include <string>
#include <srs_core_reload.hpp>
// default vhost for rtmp
#define RTMP_VHOST_DEFAULT "__defaultVhost__"
#define SRS_LOCALHOST "127.0.0.1"
#define RTMP_DEFAULT_PORT 1935
#define RTMP_DEFAULT_PORTS "1935"
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
#define SRS_CONF_DEFAULT_HLS_WINDOW 60
// in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100
// in ms, for HLS aac flush the audio
#define SRS_CONF_DEFAULT_AAC_DELAY 300
class
SrsFileBuffer
{
public
:
int
fd
;
int
line
;
// start of buffer.
char
*
start
;
// end of buffer.
char
*
end
;
// current consumed position.
char
*
pos
;
// last available position.
char
*
last
;
SrsFileBuffer
();
virtual
~
SrsFileBuffer
();
virtual
int
open
(
const
char
*
filename
);
};
class
SrsConfDirective
{
public
:
int
conf_line
;
std
::
string
name
;
std
::
vector
<
std
::
string
>
args
;
std
::
vector
<
SrsConfDirective
*>
directives
;
public
:
SrsConfDirective
();
virtual
~
SrsConfDirective
();
std
::
string
arg0
();
std
::
string
arg1
();
std
::
string
arg2
();
SrsConfDirective
*
at
(
int
index
);
SrsConfDirective
*
get
(
std
::
string
_name
);
public
:
virtual
int
parse
(
const
char
*
filename
);
public
:
enum
SrsDirectiveType
{
parse_file
,
parse_block
};
virtual
int
parse_conf
(
SrsFileBuffer
*
buffer
,
SrsDirectiveType
type
);
virtual
int
read_token
(
SrsFileBuffer
*
buffer
,
std
::
vector
<
std
::
string
>&
args
);
virtual
int
refill_buffer
(
SrsFileBuffer
*
buffer
,
bool
d_quoted
,
bool
s_quoted
,
int
startline
,
char
*&
pstart
);
};
/**
* the config parser.
* for the config supports reload, so never keep the reference cross st-thread,
* that is, never save the SrsConfDirective* get by any api of config,
* for it maybe free in the reload st-thread cycle.
* you can keep it before st-thread switch, or simply never keep it.
*/
class
SrsConfig
{
private
:
bool
show_help
;
bool
show_version
;
std
::
string
config_file
;
SrsConfDirective
*
root
;
std
::
vector
<
SrsReloadHandler
*>
subscribes
;
public
:
SrsConfig
();
virtual
~
SrsConfig
();
public
:
virtual
int
reload
();
virtual
void
subscribe
(
SrsReloadHandler
*
handler
);
virtual
void
unsubscribe
(
SrsReloadHandler
*
handler
);
public
:
virtual
int
parse_options
(
int
argc
,
char
**
argv
);
public
:
virtual
SrsConfDirective
*
get_vhost
(
std
::
string
vhost
);
virtual
bool
get_vhost_enabled
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_vhost_on_connect
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_transcode
(
std
::
string
vhost
,
std
::
string
scope
);
virtual
bool
get_transcode_enabled
(
SrsConfDirective
*
transcode
);
virtual
std
::
string
get_transcode_ffmpeg
(
SrsConfDirective
*
transcode
);
virtual
void
get_transcode_engines
(
SrsConfDirective
*
transcode
,
std
::
vector
<
SrsConfDirective
*>&
engines
);
virtual
bool
get_engine_enabled
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vcodec
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vbitrate
(
SrsConfDirective
*
engine
);
virtual
double
get_engine_vfps
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vwidth
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vheight
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_vthreads
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vprofile
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_engine_vpreset
(
SrsConfDirective
*
engine
);
virtual
void
get_engine_vparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vparams
);
virtual
void
get_engine_vfilter
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
vfilter
);
virtual
std
::
string
get_engine_acodec
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_abitrate
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_asample_rate
(
SrsConfDirective
*
engine
);
virtual
int
get_engine_achannels
(
SrsConfDirective
*
engine
);
virtual
void
get_engine_aparams
(
SrsConfDirective
*
engine
,
std
::
vector
<
std
::
string
>&
aparams
);
virtual
std
::
string
get_engine_output
(
SrsConfDirective
*
engine
);
virtual
std
::
string
get_log_dir
();
virtual
int
get_max_connections
();
virtual
SrsConfDirective
*
get_gop_cache
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_forward
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls
(
std
::
string
vhost
);
virtual
bool
get_hls_enabled
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_path
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_fragment
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_hls_window
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer_play
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_refer_publish
(
std
::
string
vhost
);
virtual
SrsConfDirective
*
get_listen
();
virtual
SrsConfDirective
*
get_chunk_size
();
virtual
SrsConfDirective
*
get_pithy_print_publish
();
virtual
SrsConfDirective
*
get_pithy_print_forwarder
();
virtual
SrsConfDirective
*
get_pithy_print_encoder
();
virtual
SrsConfDirective
*
get_pithy_print_hls
();
virtual
SrsConfDirective
*
get_pithy_print_play
();
private
:
virtual
int
parse_file
(
const
char
*
filename
);
virtual
int
parse_argv
(
int
&
i
,
char
**
argv
);
virtual
void
print_help
(
char
**
argv
);
};
/**
* deep compare directive.
*/
bool
srs_directive_equals
(
SrsConfDirective
*
a
,
SrsConfDirective
*
b
);
// global config
extern
SrsConfig
*
config
;
#endif
\ No newline at end of file
...
...
请
注册
或
登录
后发表评论