wenjiegit

merged master

要显示太多修改。

为保证性能只显示 42 of 42+ 个文件。

... ... @@ -3,7 +3,7 @@ simple-rtmp-server
srs(simple rtmp origin live server) over state-threads.<br/>
srs is a simple, high-performance, running in single process, origin live server.<br/>
srs supports rtmp, HLS, transcoding, forward, http hooks. <br/>
srs supports vhost, rtmp, HLS, transcoding, forward, http hooks. <br/>
blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/>
see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) <br/>
see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
... ... @@ -12,30 +12,36 @@ see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.githu
winlin(winterserver): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
### Usage
step 1: build srs <br/>
<strong>step 1:</strong> build srs <br/>
<pre>
tar xf simple-rtmp-server-*.*.tar.gz
cd simple-rtmp-server-*.*/trunk
./configure --with-ssl --with-hls --with-ffmpeg --with-http
make
</pre>
step 2: start srs <br/>
or get the latest code:<br/>
<pre>
./objs/simple_rtmp_server -c conf/srs.conf
git clone https://github.com/winlinvip/simple-rtmp-server
cd simple-rtmp-server/trunk
./configure --with-ssl --with-hls --with-ffmpeg --with-http
</pre>
<strong>step 2:</strong> start srs <br/>
<pre>
./objs/srs -c conf/srs.conf
</pre>
step 3(optinal): start srs listen at 19350 to forward to<br/>
<strong>step 3(optinal):</strong> start srs listen at 19350 to forward to<br/>
<pre>
./objs/simple_rtmp_server -c conf/srs.19350.conf
./objs/srs -c conf/srs.19350.conf
</pre>
step 4(optional): start nginx for HLS <br/>
<strong>step 4(optinal):</strong> start nginx for HLS <br/>
<pre>
sudo ./objs/nginx/sbin/nginx
</pre>
step 5(optional): start http hooks for srs callback <br/>
<strong>step 5(optinal):</strong> start http hooks for srs callback <br/>
<pre>
python ./research/api-server/server.py 8085
</pre>
step 6: publish live stream <br/>
<strong>step 6:</strong> publish live stream <br/>
<pre>
FMS URL: rtmp://127.0.0.1:1935/live
Stream: livestream
... ... @@ -47,7 +53,7 @@ For example, use ffmpeg to publish:
sleep 1; \
done
</pre>
step 7: add server ip to client hosts as demo. <br/>
<strong>step 7:</strong> add server ip to client hosts as demo. <br/>
<pre>
# edit the folowing file:
# linux: /etc/hosts
... ... @@ -55,26 +61,41 @@ step 7: add server ip to client hosts as demo. <br/>
# where server ip is 192.168.2.111
192.168.2.111 demo
</pre>
step 8: play live stream. <br/>
<strong>step 8:</strong> play live stream. <br/>
<pre>
players: http://demo:80/players
rtmp url: rtmp://demo:1935/live/livestream
m3u8 url: http://demo:80/live/livestream.m3u8
for android: http://demo:80/live/livestream.html
</pre>
step 9: play live stream auto transcoded<br/>
<strong>step 9(optinal):</strong> play live stream auto transcoded<br/>
<pre>
rtmp url: rtmp://demo:1935/live/livestream_ld
m3u8 url: http://demo:80/live/livestream_ld.m3u8
for android: http://demo:80/live/livestream_ld.html
rtmp url: rtmp://demo:1935/live/livestream_sd
m3u8 url: http://demo:80/live/livestream_sd.m3u8
for android: http://demo:80/live/livestream_sd.html
</pre>
step 10: play live stream auto forwarded, the hls dir change to /forward<br/>
<strong>step 10(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
<pre>
rtmp url: rtmp://demo:19350/live/livestream
m3u8 url: http://demo:80/forward/live/livestream.m3u8
for android: http://demo:80/forward/live/livestream.html
rtmp url: rtmp://demo:19350/live/livestream_ld
m3u8 url: http://demo:80/forward/live/livestream_ld.m3u8
for android: http://demo:80/forward/live/livestream_ld.html
rtmp url: rtmp://demo:19350/live/livestream_sd
m3u8 url: http://demo:80/forward/live/livestream_sd.m3u8
for android: http://demo:80/forward/live/livestream_sd.html
</pre>
<strong>step 11(optinal):</strong> modify the config and reload it (all features support reload)<br/>
<pre>
killall -1 srs
</pre>
or use specified signal to reload:<br/>
<pre>
killall -s SIGHUP srs
</pre>
### Architecture
... ... @@ -192,7 +213,15 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
* nginx v1.5.0: 139524 lines <br/>
### History
* v0.8, 2013-12-08, v0.8 released. 19186 lines.
* v0.9, 2013-12-15, ensure the HLS(ts) is continous when republish stream.
* v0.9, 2013-12-15, fix the hls reload bug, feed it the sequence header.
* v0.9, 2013-12-15, refine protocol, use int64_t timestamp for ts and jitter.
* v0.9, 2013-12-15, support set the live queue length(in seconds), drop when full.
* v0.9, 2013-12-15, fix the forwarder reconnect bug, feed it the sequence header.
* v0.9, 2013-12-15, support reload the hls/forwarder/transcoder.
* v0.9, 2013-12-14, refine the thread model for the retry threads.
* v0.9, 2013-12-10, auto install depends tools/libs on centos/ubuntu.
* v0.8, 2013-12-08, [v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8) released. 19186 lines.
* v0.8, 2013-12-08, support http hooks: on_connect/close/publish/unpublish/play/stop.
* v0.8, 2013-12-08, support multiple http hooks for a event.
* v0.8, 2013-12-07, support http callback hooks, on_connect.
... ... @@ -201,32 +230,32 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
* v0.8, 2013-12-06, support max_connections, drop if exceed.
* v0.8, 2013-12-05, support log_dir, write ffmpeg log to file.
* v0.8, 2013-12-05, fix the forward/hls/encoder bug.
* v0.7, 2013-12-03, v0.7 released. 17605 lines.
* v0.7, 2013-12-03, [v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7) released. 17605 lines.
* v0.7, 2013-12-01, support dead-loop detect for forwarder and transcoder.
* v0.7, 2013-12-01, support all ffmpeg filters and params.
* v0.7, 2013-11-30, support live stream transcoder by ffmpeg.
* v0.7, 2013-11-30, support --with/without -ffmpeg, build ffmpeg-2.1.
* v0.7, 2013-11-30, add ffmpeg-2.1, x264-core138, lame-3.99.5, libaacplus-2.0.2.
* v0.6, 2013-11-29, v0.6 released. 16094 lines.
* v0.6, 2013-11-29, [v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6) released. 16094 lines.
* v0.6, 2013-11-29, add performance summary, 1800 clients, 900Mbps, CPU 90.2%, 41MB.
* v0.6, 2013-11-29, support forward stream to other edge server.
* v0.6, 2013-11-29, support forward stream to other origin server.
* v0.6, 2013-11-28, fix memory leak bug, aac decode bug.
* v0.6, 2013-11-27, support --with or --without -hls and -ssl options.
* v0.6, 2013-11-27, support AAC 44100HZ sample rate for iphone, adjust the timestamp.
* v0.5, 2013-11-26, v0.5 released. 14449 lines.
* v0.5, 2013-11-26, [v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5) released. 14449 lines.
* v0.5, 2013-11-24, support HLS(m3u8), fragment and window.
* v0.5, 2013-11-24, support record to ts file for HLS.
* v0.5, 2013-11-21, add ts_info tool to demux ts file.
* v0.5, 2013-11-16, add rtmp players(OSMF/jwplayer5/jwplayer6).
* v0.4, 2013-11-10, v0.4 released. 12500 lines.
* v0.4, 2013-11-10, [v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4) released. 12500 lines.
* v0.4, 2013-11-10, support config and reload the pithy print.
* v0.4, 2013-11-09, support reload config(vhost and its detail).
* v0.4, 2013-11-09, support reload config(listen and chunk_size) by SIGHUP(1).
* v0.4, 2013-11-09, support longtime(>4.6hours) publish/play.
* v0.4, 2013-11-09, support config the chunk_size.
* v0.4, 2013-11-09, support pause for live stream.
* v0.3, 2013-11-04, v0.3 released. 11773 lines.
* v0.3, 2013-11-04, [v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3) released. 11773 lines.
* v0.3, 2013-11-04, support refer/play-refer/publish-refer.
* v0.3, 2013-11-04, support vhosts specified config.
* v0.3, 2013-11-02, support listen multiple ports.
... ... @@ -234,12 +263,12 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
* v0.3, 2013-10-29, support pithy print log message specified by stage.
* v0.3, 2013-10-28, support librtmp without extended-timestamp in 0xCX chunk packet.
* v0.3, 2013-10-27, support cache last gop for client fast startup.
* v0.2, 2013-10-25, v0.2 released. 10125 lines.
* v0.2, 2013-10-25, [v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2) released. 10125 lines.
* v0.2, 2013-10-25, support flash publish.
* v0.2, 2013-10-25, support h264/avc codec by rtmp complex handshake.
* v0.2, 2013-10-24, support time jitter detect and correct algorithm
* v0.2, 2013-10-24, support decode codec type to cache the h264/avc sequence header.
* v0.1, 2013-10-23, v0.1 released. 8287 lines.
* v0.1, 2013-10-23, [v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1) released. 8287 lines.
* v0.1, 2013-10-23, support basic amf0 codec, simplify the api using c-style api.
* v0.1, 2013-10-23, support shared ptr msg for zero memory copy.
* v0.1, 2013-10-22, support vp6 codec with rtmp protocol specified simple handshake.
... ...
... ... @@ -59,7 +59,9 @@ else
echo "build x264"
cd $ff_current_dir &&
rm -rf x264-snapshot-20131129-2245-stable && unzip -q ${ff_src_dir}/x264-snapshot-20131129-2245-stable.zip &&
cd x264-snapshot-20131129-2245-stable && ./configure --prefix=${ff_release_dir} --bit-depth=8 --enable-static && make && make install
cd x264-snapshot-20131129-2245-stable &&
./configure --prefix=${ff_release_dir} --disable-opencl --bit-depth=8 --enable-static &&
make && make install
ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build x264 failed"; exit 1; fi
fi
... ... @@ -86,7 +88,8 @@ else
--enable-postproc --enable-bzlib --enable-zlib --enable-parsers \
--enable-libfreetype \
--enable-libx264 --enable-libmp3lame --enable-libaacplus \
--enable-pthreads --extra-libs=-lpthread --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers &&
--enable-pthreads --extra-libs=-lpthread \
--enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers &&
make && make install
ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build x264 failed"; exit 1; fi
ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build ffmpeg failed"; exit 1; fi
fi
... ...
... ... @@ -3,6 +3,168 @@
# TODO: check gcc/g++
echo "check gcc/g++/gdb/make/openssl-devel"
echo "depends tools are ok"
#####################################################################################
# for Ubuntu
#####################################################################################
function Ubuntu_prepare()
{
uname -v|grep Ubuntu >/dev/null 2>&1
ret=$?; if [[ 0 -ne $ret ]]; then
return;
fi
echo "Ubuntu detected, install tools if needed"
gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install gcc"
require_sudoer "sudo apt-get install -y gcc"
sudo apt-get install -y gcc
echo "install gcc success"
fi
g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install g++"
require_sudoer "sudo apt-get install -y g++"
sudo apt-get install -y g++
echo "install g++ success"
fi
make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install make"
require_sudoer "sudo apt-get install -y make"
sudo apt-get install -y make
echo "install make success"
fi
autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install autoconf"
require_sudoer "sudo apt-get install -y autoconf"
sudo apt-get install -y autoconf
echo "install autoconf success"
fi
libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install libtool"
require_sudoer "sudo apt-get install -y libtool"
sudo apt-get install -y libtool
echo "install libtool success"
fi
if [[ ! -f /usr/include/pcre.h ]]; then
echo "install libpcre3-dev"
require_sudoer "sudo apt-get install -y libpcre3-dev"
sudo apt-get install -y libpcre3-dev
echo "install libpcre3-dev success"
fi
if [[ ! -f /usr/include/zlib.h ]]; then
echo "install zlib1g-dev"
require_sudoer "sudo apt-get install -y zlib1g-dev"
sudo apt-get install -y zlib1g-dev
echo "install zlib1g-dev success"
fi
if [[ ! -d /usr/include/freetype2 ]]; then
echo "install libfreetype6-dev"
require_sudoer "sudo apt-get install -y libfreetype6-dev"
sudo apt-get install -y libfreetype6-dev
echo "install libfreetype6-dev success"
fi
if [[ ! -d /usr/include/openssl ]]; then
echo "install libssl-dev"
require_sudoer "sudo apt-get install -y libssl-dev"
sudo apt-get install -y libssl-dev
echo "install libssl-dev success"
fi
echo "Ubuntu install tools success"
}
Ubuntu_prepare
#####################################################################################
# for Centos
#####################################################################################
function Centos_prepare()
{
if [[ ! -f /etc/redhat-release ]]; then
return;
fi
echo "Centos detected, install tools if needed"
gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install gcc"
require_sudoer "sudo yum install -y gcc"
sudo yum install -y gcc
echo "install gcc success"
fi
g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install gcc-c++"
require_sudoer "sudo yum install -y gcc-c++"
sudo yum install -y gcc-c++
echo "install gcc-c++ success"
fi
make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install make"
require_sudoer "sudo yum install -y make"
sudo yum install -y make
echo "install make success"
fi
automake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install automake"
require_sudoer "sudo yum install -y automake"
sudo yum install -y automake
echo "install automake success"
fi
autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install autoconf"
require_sudoer "sudo yum install -y autoconf"
sudo yum install -y autoconf
echo "install autoconf success"
fi
libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install libtool"
require_sudoer "sudo yum install -y libtool"
sudo yum install -y libtool
echo "install libtool success"
fi
if [[ ! -f /usr/include/pcre.h ]]; then
echo "install pcre-devel"
require_sudoer "sudo yum install -y pcre-devel"
sudo yum install -y pcre-devel
echo "install pcre-devel success"
fi
if [[ ! -f /usr/include/zlib.h ]]; then
echo "install zlib-devel"
require_sudoer "sudo yum install -y zlib-devel"
sudo yum install -y zlib-devel
echo "install zlib-devel success"
fi
if [[ ! -d /usr/include/freetype2 ]]; then
echo "install freetype-devel"
require_sudoer "sudo yum install -y freetype-devel"
sudo yum install -y freetype-devel
echo "install freetype-devel success"
fi
if [[ ! -d /usr/include/openssl ]]; then
echo "install openssl-devel"
require_sudoer "sudo yum install -y openssl-devel"
sudo yum install -y openssl-devel
echo "install openssl-devel success"
fi
echo "Centos install tools success"
}
Centos_prepare
#####################################################################################
# st-1.9
... ... @@ -48,6 +210,16 @@ fi
#####################################################################################
# nginx for HLS, nginx-1.5.0
#####################################################################################
function write_nginx_html5()
{
cat<<END >> ${html_file}
<video width="640" height="360"
autoplay controls autobuffer
src="${hls_stream}"
type="application/vnd.apple.mpegurl">
</video>
END
}
if [ $SRS_HLS = YES ]; then
if [[ -f ${SRS_OBJS}/nginx/sbin/nginx ]]; then
echo "nginx-1.5.7 is ok.";
... ... @@ -72,6 +244,17 @@ if [ $SRS_HLS = YES ]; then
# create forward dir
mkdir -p ${SRS_OBJS}/nginx/html/forward
# generate default html pages for android.
html_file=${SRS_OBJS}/nginx/html/live/livestream.html && hls_stream=livestream.m3u8 && write_nginx_html5
html_file=${SRS_OBJS}/nginx/html/live/livestream_ld.html && hls_stream=livestream_ld.m3u8 && write_nginx_html5
html_file=${SRS_OBJS}/nginx/html/live/livestream_sd.html && hls_stream=livestream_sd.m3u8 && write_nginx_html5
html_file=${SRS_OBJS}/nginx/html/forward/live/livestream.html && hls_stream=livestream.m3u8 && write_nginx_html5
html_file=${SRS_OBJS}/nginx/html/forward/live/livestream_ld.html && hls_stream=livestream_ld.m3u8 && write_nginx_html5
html_file=${SRS_OBJS}/nginx/html/forward/live/livestream_sd.html && hls_stream=livestream_sd.m3u8 && write_nginx_html5
# copy players to nginx html dir.
cp research/players ${SRS_OBJS}/nginx/html/ -r
fi
if [ $SRS_HLS = YES ]; then
... ... @@ -87,6 +270,7 @@ if [ $SRS_HTTP = YES ]; then
if [[ -f ${SRS_OBJS}/CherryPy-3.2.4/setup.py ]]; then
echo "CherryPy-3.2.4 is ok.";
else
require_sudoer "configure --with-http"
echo "install CherryPy-3.2.4";
(
sudo rm -rf ${SRS_OBJS}/CherryPy-3.2.4 && cd ${SRS_OBJS} &&
... ...
# the listen ports, split by space.
listen 1935;
<<<<<<< HEAD
=======
# the default chunk size is 128, max is 65536,
# some client does not support chunk size change,
# however, most clients supports it and it can improve
# performance about 10%.
# default: 4096
chunk_size 65000;
>>>>>>> upstream/master
# the logs dir.
# if enabled ffmpeg, each stracoding stream will create a log file.
# default: ./objs/logs
... ... @@ -21,14 +30,26 @@ vhost __defaultVhost__ {
chunk_size 65000;
enabled on;
gop_cache on;
hls on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
queue_length 30;
forward 127.0.0.1:19350;
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
http_hooks {
enabled off;
on_connect http://127.0.0.1:8085/api/v1/clients;
on_close http://127.0.0.1:8085/api/v1/clients;
on_publish http://127.0.0.1:8085/api/v1/streams;
on_unpublish http://127.0.0.1:8085/api/v1/streams;
on_play http://127.0.0.1:8085/api/v1/sessions;
on_stop http://127.0.0.1:8085/api/v1/sessions;
}
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine ld {
enabled on;
vfilter {
... ... @@ -82,11 +103,17 @@ vhost dev {
chunk_size 65000;
enabled on;
gop_cache on;
hls on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
queue_length 10;
forward 127.0.0.1:19350;
<<<<<<< HEAD
=======
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
>>>>>>> upstream/master
http_hooks {
enabled off;
on_connect http://127.0.0.1:8085/api/v1/clients;
... ... @@ -97,7 +124,7 @@ vhost dev {
on_stop http://127.0.0.1:8085/api/v1/sessions;
}
transcode {
enabled on;
enabled off;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine dev {
enabled on;
... ... @@ -641,36 +668,40 @@ vhost removed.vhost.com {
enabled off;
}
# the vhost with hls specified.
vhost no-hls.vhost.com {
# whether the hls is enabled.
# if off, donot write hls(ts and m3u8) when publish.
# default: on
hls on;
# the hls output path.
# the app dir is auto created under the hls_path.
# for example, for rtmp stream:
# rtmp://127.0.0.1/live/livestream
# http://127.0.0.1/live/livestream.m3u8
# where hls_path is /hls, srs will create the following files:
# /hls/live the app dir for all streams.
# /hls/live/livestream.m3u8 the HLS m3u8 file.
# /hls/live/livestream-1.ts the HLS media/ts file.
# in a word, the hls_path is for vhost.
# default: ./objs/nginx/html
hls_path /data/nginx/html;
# the hls fragment in seconds, the duration of a piece of ts.
# default: 10
hls_fragment 10;
# the hls window in seconds, the number of ts in m3u8.
# default: 60
hls_window 60;
vhost with-hls.vhost.com {
hls {
# whether the hls is enabled.
# if off, donot write hls(ts and m3u8) when publish.
# default: off
enabled on;
# the hls output path.
# the app dir is auto created under the hls_path.
# for example, for rtmp stream:
# rtmp://127.0.0.1/live/livestream
# http://127.0.0.1/live/livestream.m3u8
# where hls_path is /hls, srs will create the following files:
# /hls/live the app dir for all streams.
# /hls/live/livestream.m3u8 the HLS m3u8 file.
# /hls/live/livestream-1.ts the HLS media/ts file.
# in a word, the hls_path is for vhost.
# default: ./objs/nginx/html
hls_path /data/nginx/html;
# the hls fragment in seconds, the duration of a piece of ts.
# default: 10
hls_fragment 10;
# the hls window in seconds, the number of ts in m3u8.
# default: 60
hls_window 60;
}
}
# the vhost with hls disabled.
vhost no-hls.vhost.com {
# whether the hls is enabled.
# if off, donot write hls(ts and m3u8) when publish.
# default: on
hls off;
hls {
# whether the hls is enabled.
# if off, donot write hls(ts and m3u8) when publish.
# default: off
enabled off;
}
}
# the vhost for min delay, donot cache any stream.
vhost min.delay.com {
... ... @@ -683,6 +714,11 @@ vhost min.delay.com {
# set to on if requires client fast startup.
# default: on
gop_cache off;
# the max live queue length in seconds.
# if the messages in the queue exceed the max length,
# drop the old whole gop.
# default: 30
queue_length 10;
}
# the vhost for antisuck.
vhost refer.anti_suck.com {
... ...
... ... @@ -15,14 +15,15 @@ BLACK="\\e[0m"
# parse user options.
. auto/options.sh
# if specifies http, requires sudo to install the CherryPy.
if [ $SRS_HTTP = YES ]; then
function require_sudoer()
{
sudo echo "" >/dev/null 2>&1
ret=$?; if [[ 0 -ne $ret ]]; then echo
"--with-http requires sudoer, ret=$ret";
ret=$?; if [[ 0 -ne $ret ]]; then
echo "\"$1\" require sudoer failed. ret=$ret";
exit $ret;
fi
fi
}
# clean the exists
if [[ -f Makefile ]]; then
... ... @@ -56,11 +57,11 @@ help:
@echo " server build the srs(simple rtmp server) over st(state-threads)"
clean:
(rm -f Makefile; cd ${SRS_OBJS}; rm -rf Makefile *.hpp src st_*_load)
(rm -f Makefile; cd ${SRS_OBJS}; rm -rf srs Makefile *.hpp src st_*_load)
server: _prepare_dir
@echo "build the srs(simple rtmp server) over st(state-threads)"
\$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} simple_rtmp_server
\$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} srs
# the ./configure will generate it.
_prepare_dir:
... ... @@ -86,7 +87,7 @@ GCC = g++
LINK = \$(GCC)
AR = ar
.PHONY: default simple_rtmp_server
.PHONY: default srs
default:
... ... @@ -115,7 +116,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server"
"srs_core_handshake" "srs_core_pithy_print"
"srs_core_config" "srs_core_refer" "srs_core_reload"
"srs_core_hls" "srs_core_forward" "srs_core_encoder"
"srs_core_http")
"srs_core_http" "srs_core_thread")
MODULE_DIR="src/core" . auto/modules.sh
CORE_OBJS="${MODULE_OBJS[@]}"
... ... @@ -141,7 +142,7 @@ if [ $SRS_SSL = YES ]; then
else
LINK_OPTIONS="-ldl"
fi
BUILD_KEY="simple_rtmp_server" APP_MAIN="srs_main_server" APP_NAME="simple_rtmp_server" SO_PATH="" . auto/apps.sh
BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" SO_PATH="" . auto/apps.sh
echo 'configure ok! '
... ... @@ -179,6 +180,7 @@ echo "\" make \" to build the srs(simple rtmp server)."
echo "\" make help \" to get the usage of make"
if [ $SRS_HLS = YES ]; then
echo "\" sudo ./objs/nginx/sbin/nginx \" to start the nginx http server for hls"
echo "\" http://demo:80/players \" rtmp players(OSMF/JWPlayer)"
fi
if [ $SRS_FFMPEG = YES ]; then
echo -e "\" ./objs/ffmpeg/bin/ffmpeg \" is used for live stream transcoding"
... ... @@ -186,4 +188,4 @@ fi
if [ $SRS_HTTP = YES ]; then
echo -e "\" python ./research/api-server/server.py 8085 \" to start the api-server"
fi
echo "\" ./objs/simple_rtmp_server -c conf/srs.conf \" to start the srs live server"
echo "\" ./objs/srs -c conf/srs.conf \" to start the srs live server"
... ...
... ... @@ -9,4 +9,5 @@
<p><a href="osmf/index.html">OSMF播放器</a></p>
<p><a href="jwplayer5/index.html">JWPlayer5</a></p>
<p><a href="jwplayer6/index.html">JWPlayer6</a></p>
<p><a href="http://www.videolan.org/vlc/">VLC for HLS/rtmp/rtsp/http....</a></p>
</body>
... ...
... ... @@ -17,7 +17,7 @@
<div class="main">
<div id="player"></div>
<div class="control" id="control">
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://dev:1935/live/livestream"></input>
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
<input type="button" class="play" value="Play" onclick="play()"></input>
</div>
</div>
... ...
... ... @@ -17,7 +17,7 @@
<div class="main">
<div id="player"></div>
<div class="control" id="control">
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://dev:1935/live/livestream"></input>
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
<input type="button" class="play" value="Play" onclick="play()"></input>
</div>
</div>
... ...
... ... @@ -19,7 +19,7 @@ div.control{padding-bottom:10px; background-color:#333333; }
<div class="main" id="main">
<div id="player" class="player"></div>
<div class="control" id="control">
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://dev:1935/live/livestream"></input>
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
<select class="type" id="type">
<option value="live" selected>live</option>
<option value="recorded">vod</option>
... ...
不能预览此文件类型
... ... @@ -21,7 +21,7 @@
<script type="text/javascript">
var o = new RtmpPlayer("player", "RtmpPlayer.swf", 1350, 1050);
o.setRtmpUrl("rtmp://dev:1935/live/livestream");
o.setRtmpUrl("rtmp://demo:1935/live/livestream");
o.admin = "admin";
o.password = "123456";
o.loop = false;
... ...
... ... @@ -111,3 +111,17 @@ void srs_vhost_resolve(std::string& vhost, std::string& app)
}
}
}
void srs_close_stfd(st_netfd_t& stfd)
{
if (stfd) {
int fd = st_netfd_fileno(stfd);
st_netfd_close(stfd);
stfd = NULL;
// st does not close it sometimes,
// close it manually.
close(fd);
}
}
... ...
... ... @@ -46,6 +46,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <stddef.h>
#include <sys/types.h>
#include <st.h>
// generated by configure.
#include <srs_auto_headers.hpp>
... ... @@ -102,4 +104,7 @@ extern std::string srs_dns_resolve(std::string host);
// app...vhost...request_vhost
extern void srs_vhost_resolve(std::string& vhost, std::string& app);
// close the netfd, and close the underlayer fd.
extern void srs_close_stfd(st_netfd_t& stfd);
#endif
... ...
... ... @@ -26,6 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <arpa/inet.h>
#include <stdlib.h>
using namespace std;
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_rtmp.hpp>
... ... @@ -55,10 +57,14 @@ SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
#ifdef SRS_HTTP
http_hooks = new SrsHttpHooks();
#endif
config->subscribe(this);
}
SrsClient::~SrsClient()
{
config->unsubscribe(this);
srs_freepa(ip);
srs_freep(req);
srs_freep(res);
... ... @@ -113,6 +119,23 @@ int SrsClient::do_cycle()
return ret;
}
int SrsClient::on_reload_vhost_removed(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
// if the vhost connected is removed, disconnect the client.
srs_trace("vhost %s removed/disabled, close client url=%s",
vhost.c_str(), req->get_stream_url().c_str());
srs_close_stfd(stfd);
return ret;
}
int SrsClient::service_cycle()
{
... ... @@ -180,11 +203,7 @@ int SrsClient::service_cycle()
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(req->vhost);
if (conf && !conf->arg0().empty()) {
chunk_size = ::atoi(conf->arg0().c_str());
}
int chunk_size = config->get_chunk_size();
if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) {
srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);
return ret;
... ... @@ -192,7 +211,7 @@ int SrsClient::service_cycle()
srs_trace("set chunk_size=%d success", chunk_size);
// find a source to publish.
SrsSource* source = SrsSource::find(req->get_stream_url());
SrsSource* source = SrsSource::find(req);
srs_assert(source != NULL);
// check publish available.
... ... @@ -205,14 +224,9 @@ int SrsClient::service_cycle()
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);
bool enabled_cache = config->get_gop_cache(req->vhost);
srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);
source->set_cache(enabled_cache);
switch (type) {
case SrsClientPlay: {
... ...
... ... @@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <srs_core_conn.hpp>
#include <srs_core_reload.hpp>
class SrsRtmp;
class SrsRequest;
... ... @@ -46,7 +47,7 @@ class SrsHttpHooks;
/**
* the client provides the main logic control for RTMP clients.
*/
class SrsClient : public SrsConnection
class SrsClient : public SrsConnection, public ISrsReloadHandler
{
private:
char* ip;
... ... @@ -62,6 +63,9 @@ public:
virtual ~SrsClient();
protected:
virtual int do_cycle();
// interface ISrsReloadHandler
public:
virtual int on_reload_vhost_removed(std::string vhost);
private:
// when valid and connected to vhost/app, service the client.
virtual int service_cycle();
... ...
... ... @@ -35,6 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <vector>
#include <algorithm>
using namespace std;
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
... ... @@ -58,37 +59,84 @@ bool is_common_space(char ch)
return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
}
#define CONF_BUFFER_SIZE 1024 * 1024
class SrsFileBuffer
{
private:
// last available position.
char* last;
// end of buffer.
char* end;
// start of buffer.
char* start;
public:
// current consumed position.
char* pos;
// current parsed line.
int line;
SrsFileBuffer();
virtual ~SrsFileBuffer();
virtual int fullfill(const char* filename);
virtual bool empty();
};
SrsFileBuffer::SrsFileBuffer()
{
fd = -1;
line = 0;
pos = last = start = new char[CONF_BUFFER_SIZE];
end = start + CONF_BUFFER_SIZE;
pos = last = start = NULL;
end = start;
}
SrsFileBuffer::~SrsFileBuffer()
{
if (fd > 0) {
close(fd);
}
srs_freepa(start);
}
int SrsFileBuffer::open(const char* filename)
int SrsFileBuffer::fullfill(const char* filename)
{
assert(fd == -1);
int ret = ERROR_SUCCESS;
int fd = -1;
int nread = 0;
int filesize = 0;
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;
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("open conf file error. ret=%d", ret);
goto finish;
}
if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) {
ret = ERROR_SYSTEM_CONFIG_EOF;
srs_error("read conf file error. ret=%d", ret);
goto finish;
}
srs_freepa(start);
pos = last = start = new char[filesize];
end = start + filesize;
if ((nread = read(fd, start, filesize)) != filesize) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("read file read error. expect %d, actual %d bytes, ret=%d",
filesize, nread, ret);
goto finish;
}
line = 1;
return ERROR_SUCCESS;
finish:
if (fd > 0) {
::close(fd);
}
return ret;
}
bool SrsFileBuffer::empty()
{
return pos >= end;
}
SrsConfDirective::SrsConfDirective()
... ... @@ -105,7 +153,7 @@ SrsConfDirective::~SrsConfDirective()
directives.clear();
}
std::string SrsConfDirective::arg0()
string SrsConfDirective::arg0()
{
if (args.size() > 0) {
return args.at(0);
... ... @@ -114,7 +162,7 @@ std::string SrsConfDirective::arg0()
return "";
}
std::string SrsConfDirective::arg1()
string SrsConfDirective::arg1()
{
if (args.size() > 1) {
return args.at(1);
... ... @@ -123,7 +171,7 @@ std::string SrsConfDirective::arg1()
return "";
}
std::string SrsConfDirective::arg2()
string SrsConfDirective::arg2()
{
if (args.size() > 2) {
return args.at(2);
... ... @@ -137,7 +185,7 @@ SrsConfDirective* SrsConfDirective::at(int index)
return directives.at(index);
}
SrsConfDirective* SrsConfDirective::get(std::string _name)
SrsConfDirective* SrsConfDirective::get(string _name)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
... ... @@ -150,13 +198,26 @@ SrsConfDirective* SrsConfDirective::get(std::string _name)
return NULL;
}
SrsConfDirective* SrsConfDirective::get(string _name, string _arg0)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
if (directive->name == _name && directive->arg0() == _arg0) {
return directive;
}
}
return NULL;
}
int SrsConfDirective::parse(const char* filename)
{
int ret = ERROR_SUCCESS;
SrsFileBuffer buffer;
if ((ret = buffer.open(filename)) != ERROR_SUCCESS) {
if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) {
return ret;
}
... ... @@ -169,7 +230,7 @@ int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
int ret = ERROR_SUCCESS;
while (true) {
std::vector<std::string> args;
std::vector<string> args;
ret = read_token(buffer, args);
/**
... ... @@ -224,7 +285,7 @@ int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
}
// see: ngx_conf_read_token
int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>& args)
int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<string>& args)
{
int ret = ERROR_SUCCESS;
... ... @@ -240,11 +301,15 @@ int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>
bool last_space = true;
while (true) {
if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) {
if (buffer->empty()) {
ret = ERROR_SYSTEM_CONFIG_EOF;
if (!args.empty() || !last_space) {
srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
srs_error("end of file. ret=%d", ret);
return ret;
}
... ... @@ -344,7 +409,7 @@ int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>
memcpy(word, pstart, len);
word[len - 1] = 0;
std::string word_str = word;
string word_str = word;
if (!word_str.empty()) {
args.push_back(word_str);
}
... ... @@ -363,59 +428,6 @@ int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>
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()
... ... @@ -453,12 +465,12 @@ int SrsConfig::reload()
conf.root = NULL;
// merge config.
std::vector<SrsReloadHandler*>::iterator it;
std::vector<ISrsReloadHandler*>::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;
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
srs_error("notify subscribes reload listen failed. ret=%d", ret);
return ret;
... ... @@ -466,10 +478,11 @@ int SrsConfig::reload()
}
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;
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
return ret;
... ... @@ -477,15 +490,121 @@ int SrsConfig::reload()
}
srs_trace("reload pithy_print success.");
}
// TODO: suppor reload hls/forward/ffmpeg/http
// merge config: vhost added, directly supported.
// merge config: vhost removed/disabled/modified.
for (int i = 0; i < (int)old_root->directives.size(); i++) {
SrsConfDirective* old_vhost = old_root->at(i);
// only process vhost directives.
if (old_vhost->name != "vhost") {
continue;
}
std::string vhost = old_vhost->arg0();
SrsConfDirective* new_vhost = root->get("vhost", vhost);
// ignore if absolutely equal
if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) {
srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str());
continue;
}
// ignore if enable the new vhost when old vhost is disabled.
if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str());
continue;
}
// ignore if both old and new vhost are disabled.
if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str());
continue;
}
// merge config: vhost removed/disabled.
if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled, reload it.", vhost.c_str());
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print remove "
"vhost %s failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("reload remove vhost %s success.", vhost.c_str());
}
// merge config: vhost modified.
srs_trace("vhost %s modified, reload its detail.", vhost.c_str());
if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
// gop_cache
if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload gop_cache success.", vhost.c_str());
}
// queue_length
if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload queue_length success.", vhost.c_str());
}
// forward
if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload forward success.", vhost.c_str());
}
// hls
if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload hls success.", vhost.c_str());
}
// transcode
if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload transcode success.", vhost.c_str());
}
// TODO: suppor reload hls/forward/ffmpeg/http
continue;
}
srs_warn("invalid reload path, enabled old: %d, new: %d",
get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost));
}
return ret;
}
void SrsConfig::subscribe(SrsReloadHandler* handler)
void SrsConfig::subscribe(ISrsReloadHandler* handler)
{
std::vector<SrsReloadHandler*>::iterator it;
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it != subscribes.end()) {
... ... @@ -495,9 +614,9 @@ void SrsConfig::subscribe(SrsReloadHandler* handler)
subscribes.push_back(handler);
}
void SrsConfig::unsubscribe(SrsReloadHandler* handler)
void SrsConfig::unsubscribe(ISrsReloadHandler* handler)
{
std::vector<SrsReloadHandler*>::iterator it;
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it == subscribes.end()) {
... ... @@ -539,7 +658,102 @@ int SrsConfig::parse_options(int argc, char** argv)
return parse_file(config_file.c_str());
}
SrsConfDirective* SrsConfig::get_vhost(const std::string& vhost)
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 forward.
// 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]);
}
SrsConfDirective* SrsConfig::get_vhost(string vhost)
{
srs_assert(root);
... ... @@ -562,7 +776,7 @@ SrsConfDirective* SrsConfig::get_vhost(const std::string& vhost)
return NULL;
}
SrsConfDirective* SrsConfig::get_vhost_on_connect(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -583,7 +797,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_connect(const std::string &vhost)
return conf->get("on_connect");
}
SrsConfDirective* SrsConfig::get_vhost_on_close(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -604,7 +818,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_close(const std::string &vhost)
return conf->get("on_close");
}
SrsConfDirective* SrsConfig::get_vhost_on_publish(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -625,7 +839,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_publish(const std::string &vhost)
return conf->get("on_publish");
}
SrsConfDirective* SrsConfig::get_vhost_on_unpublish(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -646,7 +860,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_unpublish(const std::string &vhost)
return conf->get("on_unpublish");
}
SrsConfDirective* SrsConfig::get_vhost_on_play(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -667,7 +881,7 @@ SrsConfDirective* SrsConfig::get_vhost_on_play(const std::string &vhost)
return conf->get("on_play");
}
SrsConfDirective* SrsConfig::get_vhost_on_stop(const std::string &vhost)
SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -688,15 +902,20 @@ SrsConfDirective* SrsConfig::get_vhost_on_stop(const std::string &vhost)
return conf->get("on_stop");
}
bool SrsConfig::get_vhost_enabled(const std::string &vhost)
bool SrsConfig::get_vhost_enabled(string vhost)
{
SrsConfDirective* vhost_conf = get_vhost(vhost);
return get_vhost_enabled(vhost_conf);
}
if (!vhost_conf) {
return true;
bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost)
{
if (!vhost) {
return false;
}
SrsConfDirective* conf = vhost_conf->get("enabled");
SrsConfDirective* conf = vhost->get("enabled");
if (!conf) {
return true;
}
... ... @@ -708,7 +927,7 @@ bool SrsConfig::get_vhost_enabled(const std::string &vhost)
return true;
}
SrsConfDirective* SrsConfig::get_transcode(const std::string &vhost, const std::string &scope)
SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -742,7 +961,7 @@ bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
return true;
}
std::string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
{
if (!transcode) {
return "";
... ... @@ -787,7 +1006,7 @@ bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
return true;
}
std::string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
... ... @@ -871,7 +1090,7 @@ int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
return ::atoi(conf->arg0().c_str());
}
std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
{
if (!engine) {
return "";
... ... @@ -885,7 +1104,7 @@ std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
return conf->arg0();
}
std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
{
if (!engine) {
return "";
... ... @@ -899,7 +1118,7 @@ std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
return conf->arg0();
}
void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams)
void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<string>& vparams)
{
if (!engine) {
return;
... ... @@ -921,7 +1140,7 @@ void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<std::st
}
}
void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter)
void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<string>& vfilter)
{
if (!engine) {
return;
... ... @@ -943,7 +1162,7 @@ void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<std::st
}
}
std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
... ... @@ -999,7 +1218,7 @@ int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
return ::atoi(conf->arg0().c_str());
}
void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams)
void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<string>& aparams)
{
if (!engine) {
return;
... ... @@ -1021,7 +1240,7 @@ void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<std::st
}
}
std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
string SrsConfig::get_engine_output(SrsConfDirective* engine)
{
if (!engine) {
return "";
... ... @@ -1035,7 +1254,7 @@ std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
return conf->arg0();
}
std::string SrsConfig::get_log_dir()
string SrsConfig::get_log_dir()
{
srs_assert(root);
... ... @@ -1059,18 +1278,39 @@ int SrsConfig::get_max_connections()
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_gop_cache(const std::string &vhost)
bool SrsConfig::get_gop_cache(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
return true;
}
conf = conf->get("gop_cache");
if (conf && conf->arg0() == "off") {
return false;
}
return conf->get("gop_cache");
return true;
}
SrsConfDirective* SrsConfig::get_forward(const std::string &vhost)
double SrsConfig::get_queue_length(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
conf = conf->get("queue_length");
if (!conf || conf->arg0().empty()) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_forward(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -1081,7 +1321,7 @@ SrsConfDirective* SrsConfig::get_forward(const std::string &vhost)
return conf->get("forward");
}
SrsConfDirective* SrsConfig::get_hls(const std::string &vhost)
SrsConfDirective* SrsConfig::get_hls(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -1092,55 +1332,79 @@ SrsConfDirective* SrsConfig::get_hls(const std::string &vhost)
return conf->get("hls");
}
bool SrsConfig::get_hls_enabled(const std::string &vhost)
bool SrsConfig::get_hls_enabled(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return true;
return false;
}
if (hls->arg0() == "off") {
SrsConfDirective* conf = hls->get("enabled");
if (!conf) {
return false;
}
return true;
if (conf->arg0() == "on") {
return true;
}
return false;
}
SrsConfDirective* SrsConfig::get_hls_path(const std::string &vhost)
string SrsConfig::get_hls_path(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
return conf->get("hls_path");
SrsConfDirective* conf = hls->get("hls_path");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
return conf->arg0();
}
SrsConfDirective* SrsConfig::get_hls_fragment(const std::string &vhost)
double SrsConfig::get_hls_fragment(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
return conf->get("hls_fragment");
SrsConfDirective* conf = hls->get("hls_fragment");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
return ::atof(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_hls_window(const std::string &vhost)
double SrsConfig::get_hls_window(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
return conf->get("hls_window");
SrsConfDirective* conf = hls->get("hls_window");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
return ::atof(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_refer(const std::string &vhost)
SrsConfDirective* SrsConfig::get_refer(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -1151,7 +1415,7 @@ SrsConfDirective* SrsConfig::get_refer(const std::string &vhost)
return conf->get("refer");
}
SrsConfDirective* SrsConfig::get_refer_play(const std::string &vhost)
SrsConfDirective* SrsConfig::get_refer_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -1162,7 +1426,7 @@ SrsConfDirective* SrsConfig::get_refer_play(const std::string &vhost)
return conf->get("refer_play");
}
SrsConfDirective* SrsConfig::get_refer_publish(const std::string &vhost)
SrsConfDirective* SrsConfig::get_refer_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
... ... @@ -1178,65 +1442,59 @@ SrsConfDirective* SrsConfig::get_listen()
return root->get("listen");
}
SrsConfDirective* SrsConfig::get_chunk_size(const std::string &vhost)
int SrsConfig::get_chunk_size()
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("chunk_size");
SrsConfDirective* conf = root->get("chunk_size");
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_pithy_print_publish()
int SrsConfig::get_pithy_print_publish()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return NULL;
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
return pithy->get("publish");
}
SrsConfDirective* SrsConfig::get_pithy_print_forwarder()
{
SrsConfDirective* pithy = root->get("pithy_print");
pithy = pithy->get("publish");
if (!pithy) {
return NULL;
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
return pithy->get("forwarder");
return ::atoi(pithy->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_pithy_print_hls()
int SrsConfig::get_pithy_print_forwarder()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return NULL;
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
return pithy->get("hls");
}
SrsConfDirective* SrsConfig::get_pithy_print_encoder()
{
SrsConfDirective* pithy = root->get("encoder");
pithy = pithy->get("forwarder");
if (!pithy) {
return NULL;
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
return pithy->get("forwarder");
return ::atoi(pithy->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_pithy_print_play()
int SrsConfig::get_pithy_print_hls()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return NULL;
return SRS_STAGE_HLS_INTERVAL_MS;
}
pithy = pithy->get("hls");
if (!pithy) {
return SRS_STAGE_HLS_INTERVAL_MS;
}
return pithy->get("play");
return ::atoi(pithy->arg0().c_str());
}
bool SrsConfig::get_bw_check_enabled(const std::string &vhost, const std::string &key)
... ... @@ -1295,104 +1553,43 @@ void SrsConfig::get_bw_check_settings(const std::string &vhost, int64_t &interva
pub_kbps = ::atoi(pub_conf->arg0().c_str());
}
int SrsConfig::parse_file(const char* filename)
int SrsConfig::get_pithy_print_encoder()
{
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* pithy = root->get("encoder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
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;
pithy = pithy->get("forwarder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
// TODO: check the hls.
// TODO: check other config.
// TODO: check hls.
// TODO: check ssl.
// TODO: check ffmpeg.
// TODO: check http.
return ret;
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::parse_argv(int& i, char** argv)
int SrsConfig::get_pithy_print_play()
{
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;
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
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;
}
pithy = pithy->get("play");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
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]);
return ::atoi(pithy->arg0().c_str());
}
bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
{
// both NULL, equal.
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
... ...
... ... @@ -48,25 +48,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_AAC_SYNC 100
// in ms, for HLS aac flush the audio
#define SRS_CONF_DEFAULT_AAC_DELAY 300
// in seconds, the live queue length.
#define SRS_CONF_DEFAULT_QUEUE_LENGTH 30
// in seconds, the paused queue length.
#define SRS_CONF_DEFAULT_PAUSED_LENGTH 10
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);
};
#define SRS_CONF_DEFAULT_CHUNK_SIZE 4096
#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300
#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100
#define SRS_STAGE_FORWARDER_INTERVAL_MS 2000
#define SRS_STAGE_ENCODER_INTERVAL_MS 2000
#define SRS_STAGE_HLS_INTERVAL_MS 2000
class SrsFileBuffer;
class SrsConfDirective
{
... ... @@ -83,13 +78,13 @@ public:
std::string arg2();
SrsConfDirective* at(int index);
SrsConfDirective* get(std::string _name);
SrsConfDirective* get(std::string _name, std::string _arg0);
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);
};
/**
... ... @@ -106,71 +101,76 @@ private:
bool show_version;
std::string config_file;
SrsConfDirective* root;
std::vector<SrsReloadHandler*> subscribes;
std::vector<ISrsReloadHandler*> subscribes;
public:
SrsConfig();
virtual ~SrsConfig();
public:
virtual int reload();
virtual void subscribe(SrsReloadHandler* handler);
virtual void unsubscribe(SrsReloadHandler* handler);
virtual void subscribe(ISrsReloadHandler* handler);
virtual void unsubscribe(ISrsReloadHandler* handler);
public:
virtual int parse_options(int argc, char** argv);
public:
virtual SrsConfDirective* get_vhost(const std::string &vhost);
virtual bool get_vhost_enabled(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_connect(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_close(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_publish(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_unpublish(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_play(const std::string& vhost);
virtual SrsConfDirective* get_vhost_on_stop(const std::string& vhost);
virtual SrsConfDirective* get_transcode(const std::string& vhost, const 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(const std::string& vhost);
virtual SrsConfDirective* get_forward(const std::string& vhost);
virtual SrsConfDirective* get_hls(const std::string &vhost);
virtual bool get_hls_enabled(const std::string& vhost);
virtual SrsConfDirective* get_hls_path(const std::string& vhost);
virtual SrsConfDirective* get_hls_fragment(const std::string& vhost);
virtual SrsConfDirective* get_hls_window(const std::string& vhost);
virtual SrsConfDirective* get_refer(const std::string& vhost);
virtual SrsConfDirective* get_refer_play(const std::string& vhost);
virtual SrsConfDirective* get_refer_publish(const std::string& vhost);
virtual SrsConfDirective* get_listen();
virtual SrsConfDirective* get_chunk_size(const std::string &vhost);
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();
virtual bool get_bw_check_enabled(const std::string &vhost, const std::string &key);
virtual void get_bw_check_settings(const std::string &vhost, int64_t &interval_ms, int &play_kbps, int &pub_kbps);
private:
virtual int parse_file(const char* filename);
virtual int parse_argv(int& i, char** argv);
virtual void print_help(char** argv);
public:
virtual SrsConfDirective* get_vhost(std::string vhost);
virtual bool get_vhost_enabled(std::string vhost);
virtual bool get_vhost_enabled(SrsConfDirective* vhost);
virtual SrsConfDirective* get_vhost_on_connect(std::string vhost);
virtual SrsConfDirective* get_vhost_on_close(std::string vhost);
virtual SrsConfDirective* get_vhost_on_publish(std::string vhost);
virtual SrsConfDirective* get_vhost_on_unpublish(std::string vhost);
virtual SrsConfDirective* get_vhost_on_play(std::string vhost);
virtual SrsConfDirective* get_vhost_on_stop(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 bool get_gop_cache(std::string vhost);
virtual double get_queue_length(std::string vhost);
virtual SrsConfDirective* get_forward(std::string vhost);
private:
virtual SrsConfDirective* get_hls(std::string vhost);
public:
virtual bool get_hls_enabled(std::string vhost);
virtual std::string get_hls_path(std::string vhost);
virtual double get_hls_fragment(std::string vhost);
virtual double 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 int get_chunk_size();
virtual int get_pithy_print_publish();
virtual int get_pithy_print_forwarder();
virtual int get_pithy_print_encoder();
virtual int get_pithy_print_hls();
virtual int get_pithy_print_play();
virtual bool get_bw_check_enabled(const std::string &vhost, const std::string &key);
virtual void get_bw_check_settings(const std::string &vhost, int64_t &interval_ms, int &play_kbps, int &pub_kbps);
};
/**
... ...
... ... @@ -36,15 +36,7 @@ SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd)
SrsConnection::~SrsConnection()
{
if (stfd) {
int fd = st_netfd_fileno(stfd);
st_netfd_close(stfd);
stfd = NULL;
// st does not close it sometimes,
// close it manually.
close(fd);
}
srs_close_stfd(stfd);
}
int SrsConnection::start()
... ...
... ... @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <st.h>
class SrsServer;
class SrsConnection
{
... ...
... ... @@ -483,52 +483,15 @@ void SrsFFMPEG::stop()
SrsEncoder::SrsEncoder()
{
tid = NULL;
loop = false;
pthread = new SrsThread(this, SRS_ENCODER_SLEEP_MS);
pithy_print = new SrsPithyPrint(SRS_STAGE_ENCODER);
}
SrsEncoder::~SrsEncoder()
{
on_unpublish();
}
int SrsEncoder::parse_scope_engines(SrsRequest* req)
{
int ret = ERROR_SUCCESS;
// parse all transcode engines.
SrsConfDirective* conf = NULL;
// parse vhost scope engines
std::string scope = "";
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse vhost scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
// parse app scope engines
scope = req->app;
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse app scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
// parse stream scope engines
scope += "/";
scope += req->stream;
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse stream scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
return ret;
srs_freep(pthread);
}
int SrsEncoder::on_publish(SrsRequest* req)
... ... @@ -539,6 +502,7 @@ int SrsEncoder::on_publish(SrsRequest* req)
// ignore the loop encoder
if (ret == ERROR_ENCODER_LOOP) {
clear_engines();
ret = ERROR_SUCCESS;
}
... ... @@ -548,9 +512,7 @@ int SrsEncoder::on_publish(SrsRequest* req)
}
// start thread to run all encoding engines.
srs_assert(!tid);
if((tid = st_thread_create(encoder_thread, this, 1, 0)) == NULL) {
ret = ERROR_ST_CREATE_FORWARD_THREAD;
if ((ret = pthread->start()) != ERROR_SUCCESS) {
srs_error("st_thread_create failed. ret=%d", ret);
return ret;
}
... ... @@ -560,23 +522,58 @@ int SrsEncoder::on_publish(SrsRequest* req)
void SrsEncoder::on_unpublish()
{
if (tid) {
loop = false;
st_thread_interrupt(tid);
st_thread_join(tid, NULL);
tid = NULL;
pthread->stop();
clear_engines();
}
int SrsEncoder::cycle()
{
int ret = ERROR_SUCCESS;
std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it;
// start all ffmpegs.
if ((ret = ffmpeg->start()) != ERROR_SUCCESS) {
srs_error("ffmpeg start failed. ret=%d", ret);
return ret;
}
// check ffmpeg status.
if ((ret = ffmpeg->cycle()) != ERROR_SUCCESS) {
srs_error("ffmpeg cycle failed. ret=%d", ret);
return ret;
}
}
clear_engines();
// pithy print
encoder();
pithy_print->elapse(SRS_ENCODER_SLEEP_MS);
return ret;
}
void SrsEncoder::on_leave_loop()
{
// kill ffmpeg when finished and it alive
std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it;
ffmpeg->stop();
}
}
void SrsEncoder::clear_engines()
{
std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it;
srs_freep(ffmpeg);
}
ffmpegs.clear();
}
... ... @@ -585,6 +582,45 @@ SrsFFMPEG* SrsEncoder::at(int index)
return ffmpegs[index];
}
int SrsEncoder::parse_scope_engines(SrsRequest* req)
{
int ret = ERROR_SUCCESS;
// parse all transcode engines.
SrsConfDirective* conf = NULL;
// parse vhost scope engines
std::string scope = "";
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse vhost scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
// parse app scope engines
scope = req->app;
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse app scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
// parse stream scope engines
scope += "/";
scope += req->stream;
if ((conf = config->get_transcode(req->vhost, scope)) != NULL) {
if ((ret = parse_transcode(req, conf)) != ERROR_SUCCESS) {
srs_error("parse stream scope=%s transcode engines failed. "
"ret=%d", scope.c_str(), ret);
return ret;
}
}
return ret;
}
int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf)
{
int ret = ERROR_SUCCESS;
... ... @@ -631,7 +667,6 @@ int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf)
// if got a loop, donot transcode the whole stream.
if (ret == ERROR_ENCODER_LOOP) {
clear_engines();
break;
}
... ... @@ -646,85 +681,14 @@ int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf)
return ret;
}
int SrsEncoder::cycle()
{
int ret = ERROR_SUCCESS;
std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it;
// start all ffmpegs.
if ((ret = ffmpeg->start()) != ERROR_SUCCESS) {
srs_error("ffmpeg start failed. ret=%d", ret);
return ret;
}
// check ffmpeg status.
if ((ret = ffmpeg->cycle()) != ERROR_SUCCESS) {
srs_error("ffmpeg cycle failed. ret=%d", ret);
return ret;
}
}
return ret;
}
void SrsEncoder::encoder_cycle()
{
int ret = ERROR_SUCCESS;
log_context->generate_id();
srs_trace("encoder cycle start");
SrsPithyPrint pithy_print(SRS_STAGE_ENCODER);
while (loop) {
if ((ret = cycle()) != ERROR_SUCCESS) {
srs_warn("encoder cycle failed, ignored and retry, ret=%d", ret);
} else {
srs_info("encoder cycle success, retry");
}
if (!loop) {
break;
}
encoder(&pithy_print);
pithy_print.elapse(SRS_ENCODER_SLEEP_MS);
st_usleep(SRS_ENCODER_SLEEP_MS * 1000);
}
// kill ffmpeg when finished and it alive
std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it;
ffmpeg->stop();
}
srs_trace("encoder cycle finished");
}
void SrsEncoder::encoder(SrsPithyPrint* pithy_print)
void SrsEncoder::encoder()
{
// reportable
if (pithy_print->can_print()) {
srs_trace("-> time=%"PRId64", encoders=%d",
pithy_print->get_age(), (int)ffmpegs.size());
// TODO: FIXME: show more info.
srs_trace("-> time=%"PRId64", encoders=%d", pithy_print->get_age(), (int)ffmpegs.size());
}
}
void* SrsEncoder::encoder_thread(void* arg)
{
SrsEncoder* obj = (SrsEncoder*)arg;
srs_assert(obj != NULL);
obj->loop = true;
obj->encoder_cycle();
return NULL;
}
#endif
... ...
... ... @@ -32,7 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <vector>
#include <st.h>
#include <srs_core_thread.hpp>
class SrsConfDirective;
class SrsRequest;
... ... @@ -85,28 +85,29 @@ public:
* the encoder for a stream,
* may use multiple ffmpegs to transcode the specified stream.
*/
class SrsEncoder
class SrsEncoder : public ISrsThreadHandler
{
private:
std::vector<SrsFFMPEG*> ffmpegs;
private:
st_thread_t tid;
bool loop;
SrsThread* pthread;
SrsPithyPrint* pithy_print;
public:
SrsEncoder();
virtual ~SrsEncoder();
public:
virtual int on_publish(SrsRequest* req);
virtual void on_unpublish();
// interface ISrsThreadHandler.
public:
virtual int cycle();
virtual void on_leave_loop();
private:
virtual int parse_scope_engines(SrsRequest* req);
virtual void clear_engines();
virtual SrsFFMPEG* at(int index);
virtual int parse_scope_engines(SrsRequest* req);
virtual int parse_transcode(SrsRequest* req, SrsConfDirective* conf);
virtual int cycle();
virtual void encoder_cycle();
virtual void encoder(SrsPithyPrint* pithy_print);
static void* encoder_thread(void* arg);
virtual void encoder();
};
#endif
... ...
... ... @@ -37,8 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_ST_OPEN_SOCKET 102
#define ERROR_ST_CREATE_LISTEN_THREAD 103
#define ERROR_ST_CREATE_CYCLE_THREAD 104
#define ERROR_ST_CREATE_FORWARD_THREAD 105
#define ERROR_ST_CONNECT 106
#define ERROR_ST_CONNECT 105
#define ERROR_SOCKET_CREATE 200
#define ERROR_SOCKET_SETREUSE 201
... ... @@ -85,9 +84,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_SYSTEM_CONFIG_EOF 409
#define ERROR_SYSTEM_STREAM_BUSY 410
#define ERROR_SYSTEM_IP_INVALID 411
#define ERROR_SYSTEM_CONFIG_TOO_LARGE 412
#define ERROR_SYSTEM_FORWARD_LOOP 413
#define ERROR_SYSTEM_WAITPID 414
#define ERROR_SYSTEM_FORWARD_LOOP 412
#define ERROR_SYSTEM_WAITPID 413
// see librtmp.
// failed when open ssl create the dh
... ...
... ... @@ -35,32 +35,39 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_pithy_print.hpp>
#include <srs_core_rtmp.hpp>
#include <srs_core_config.hpp>
#include <srs_core_source.hpp>
#include <srs_core_autofree.hpp>
#define SRS_PULSE_TIMEOUT_MS 100
#define SRS_FORWARDER_SLEEP_MS 2000
#define SRS_SEND_TIMEOUT_US 3000000L
#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
SrsForwarder::SrsForwarder()
SrsForwarder::SrsForwarder(SrsSource* _source)
{
source = _source;
client = NULL;
stfd = NULL;
stream_id = 0;
tid = NULL;
loop = false;
pthread = new SrsThread(this, SRS_FORWARDER_SLEEP_MS);
queue = new SrsMessageQueue();
jitter = new SrsRtmpJitter();
}
SrsForwarder::~SrsForwarder()
{
on_unpublish();
std::vector<SrsSharedPtrMessage*>::iterator it;
for (it = msgs.begin(); it != msgs.end(); ++it) {
SrsSharedPtrMessage* msg = *it;
srs_freep(msg);
}
msgs.clear();
srs_freep(pthread);
srs_freep(queue);
srs_freep(jitter);
}
void SrsForwarder::set_queue_size(double queue_size)
{
queue->set_queue_size(queue_size);
}
int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server)
... ... @@ -110,41 +117,19 @@ int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server)
source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(),
stream_name.c_str());
// TODO: seems bug when republish and reforward.
// start forward
if ((ret = open_socket()) != ERROR_SUCCESS) {
return ret;
}
srs_assert(!tid);
if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){
ret = ERROR_ST_CREATE_FORWARD_THREAD;
srs_error("st_thread_create failed. ret=%d", ret);
if ((ret = pthread->start()) != ERROR_SUCCESS) {
srs_error("start srs thread failed. ret=%d", ret);
return ret;
}
}
return ret;
}
void SrsForwarder::on_unpublish()
{
if (tid) {
loop = false;
st_thread_interrupt(tid);
st_thread_join(tid, NULL);
tid = NULL;
}
pthread->stop();
if (stfd) {
int fd = st_netfd_fileno(stfd);
st_netfd_close(stfd);
stfd = NULL;
// st does not close it sometimes,
// close it manually.
close(fd);
}
close_underlayer_socket();
srs_freep(client);
}
... ... @@ -153,7 +138,14 @@ int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata)
{
int ret = ERROR_SUCCESS;
msgs.push_back(metadata);
if ((ret = jitter->correct(metadata, 0, 0)) != ERROR_SUCCESS) {
srs_freep(metadata);
return ret;
}
if ((ret = queue->enqueue(metadata)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
... ... @@ -162,7 +154,14 @@ int SrsForwarder::on_audio(SrsSharedPtrMessage* msg)
{
int ret = ERROR_SUCCESS;
msgs.push_back(msg);
if ((ret = jitter->correct(msg, 0, 0)) != ERROR_SUCCESS) {
srs_freep(msg);
return ret;
}
if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
... ... @@ -171,15 +170,74 @@ int SrsForwarder::on_video(SrsSharedPtrMessage* msg)
{
int ret = ERROR_SUCCESS;
msgs.push_back(msg);
if ((ret = jitter->correct(msg, 0, 0)) != ERROR_SUCCESS) {
srs_freep(msg);
return ret;
}
if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsForwarder::open_socket()
int SrsForwarder::cycle()
{
int ret = ERROR_SUCCESS;
if ((ret = connect_server()) != ERROR_SUCCESS) {
return ret;
}
srs_assert(client);
client->set_recv_timeout(SRS_RECV_TIMEOUT_US);
client->set_send_timeout(SRS_SEND_TIMEOUT_US);
if ((ret = client->handshake()) != ERROR_SUCCESS) {
srs_error("handshake with server failed. ret=%d", ret);
return ret;
}
if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) {
srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret);
return ret;
}
if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
return ret;
}
if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) {
srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
stream_name.c_str(), stream_id, ret);
return ret;
}
if ((ret = source->on_forwarder_start(this)) != ERROR_SUCCESS) {
srs_error("callback the source to feed the sequence header failed. ret=%d", ret);
return ret;
}
if ((ret = forward()) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
void SrsForwarder::close_underlayer_socket()
{
srs_close_stfd(stfd);
}
int SrsForwarder::connect_server()
{
int ret = ERROR_SUCCESS;
// reopen
close_underlayer_socket();
// open socket.
srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d",
stream_name.c_str(), tc_url.c_str(), server.c_str(), port);
... ... @@ -190,6 +248,7 @@ int SrsForwarder::open_socket()
return ret;
}
srs_assert(!stfd);
stfd = st_netfd_open_socket(sock);
if(stfd == NULL){
ret = ERROR_ST_OPEN_SOCKET;
... ... @@ -200,13 +259,7 @@ int SrsForwarder::open_socket()
srs_freep(client);
client = new SrsRtmpClient(stfd);
return ret;
}
int SrsForwarder::connect_server()
{
int ret = ERROR_SUCCESS;
// connect to server.
std::string ip = srs_dns_resolve(server);
if (ip.empty()) {
ret = ERROR_SYSTEM_IP_INVALID;
... ... @@ -229,43 +282,6 @@ int SrsForwarder::connect_server()
return ret;
}
int SrsForwarder::cycle()
{
int ret = ERROR_SUCCESS;
client->set_recv_timeout(SRS_RECV_TIMEOUT_US);
client->set_send_timeout(SRS_SEND_TIMEOUT_US);
if ((ret = connect_server()) != ERROR_SUCCESS) {
return ret;
}
srs_assert(client);
if ((ret = client->handshake()) != ERROR_SUCCESS) {
srs_error("handshake with server failed. ret=%d", ret);
return ret;
}
if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) {
srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret);
return ret;
}
if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
return ret;
}
if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) {
srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
stream_name.c_str(), stream_id, ret);
return ret;
}
if ((ret = forward()) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsForwarder::forward()
{
int ret = ERROR_SUCCESS;
... ... @@ -274,9 +290,7 @@ int SrsForwarder::forward()
SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER);
while (loop) {
pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
while (true) {
// switch to other st-threads.
st_usleep(0);
... ... @@ -292,91 +306,42 @@ int SrsForwarder::forward()
}
}
// forward all messages.
int count = 0;
SrsSharedPtrMessage** msgs = NULL;
if ((ret = queue->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
srs_error("get message to forward failed. ret=%d", ret);
return ret;
}
// ignore when no messages.
int count = (int)msgs.size();
if (msgs.empty()) {
if (count <= 0) {
srs_verbose("no packets to forward.");
continue;
}
SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
// reportable
// pithy print
pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
if (pithy_print.can_print()) {
srs_trace("-> time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps());
}
// all msgs to forward.
int i = 0;
for (i = 0; i < count; i++) {
for (int i = 0; i < count; i++) {
SrsSharedPtrMessage* msg = msgs[i];
msgs[i] = NULL;
// we erased the sendout messages, the msg must not be NULL.
srs_assert(msg);
msgs[i] = NULL;
ret = client->send_message(msg);
if (ret != ERROR_SUCCESS) {
if ((ret = client->send_message(msg)) != ERROR_SUCCESS) {
srs_error("forwarder send message to server failed. ret=%d", ret);
// convert the index to count when error.
i++;
break;
return ret;
}
}
// clear sendout mesages.
if (i < count) {
srs_warn("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret);
} else {
srs_info("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret);
}
msgs.erase(msgs.begin(), msgs.begin() + i);
if (ret != ERROR_SUCCESS) {
break;
}
}
return ret;
}
void SrsForwarder::forward_cycle()
{
int ret = ERROR_SUCCESS;
log_context->generate_id();
srs_trace("forward cycle start");
while (loop) {
if ((ret = cycle()) != ERROR_SUCCESS) {
srs_warn("forward cycle failed, ignored and retry, ret=%d", ret);
} else {
srs_info("forward cycle success, retry");
}
if (!loop) {
break;
}
st_usleep(SRS_FORWARDER_SLEEP_MS * 1000);
if ((ret = open_socket()) != ERROR_SUCCESS) {
srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret);
} else {
srs_info("forward cycle reopen success");
}
}
srs_trace("forward cycle finished");
}
void* SrsForwarder::forward_thread(void* arg)
{
SrsForwarder* obj = (SrsForwarder*)arg;
srs_assert(obj != NULL);
obj->loop = true;
obj->forward_cycle();
return NULL;
}
... ...
... ... @@ -30,19 +30,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <string>
#include <vector>
#include <st.h>
#include <srs_core_thread.hpp>
class SrsSharedPtrMessage;
class SrsOnMetaDataPacket;
class SrsMessageQueue;
class SrsRtmpJitter;
class SrsRtmpClient;
class SrsRequest;
class SrsSource;
/**
* forward the stream to other servers.
*/
class SrsForwarder
class SrsForwarder : public ISrsThreadHandler
{
private:
std::string app;
... ... @@ -53,28 +55,30 @@ private:
int port;
private:
st_netfd_t stfd;
st_thread_t tid;
bool loop;
SrsThread* pthread;
private:
SrsSource* source;
SrsRtmpClient* client;
std::vector<SrsSharedPtrMessage*> msgs;
SrsRtmpJitter* jitter;
SrsMessageQueue* queue;
public:
SrsForwarder();
SrsForwarder(SrsSource* _source);
virtual ~SrsForwarder();
public:
virtual void set_queue_size(double queue_size);
public:
virtual int on_publish(SrsRequest* req, std::string forward_server);
virtual void on_unpublish();
virtual int on_meta_data(SrsSharedPtrMessage* metadata);
virtual int on_audio(SrsSharedPtrMessage* msg);
virtual int on_video(SrsSharedPtrMessage* msg);
// interface ISrsThreadHandler.
public:
virtual int cycle();
private:
virtual int open_socket();
virtual void close_underlayer_socket();
virtual int connect_server();
private:
virtual int cycle();
virtual int forward();
virtual void forward_cycle();
static void* forward_thread(void* arg);
};
#endif
... ...
... ... @@ -1109,10 +1109,11 @@ int SrsTSCache::cache_video(SrsCodec* codec, SrsCodecSample* sample)
return ret;
}
SrsHls::SrsHls()
SrsHls::SrsHls(SrsSource* _source)
{
hls_enabled = false;
source = _source;
codec = new SrsCodec();
sample = new SrsCodecSample();
jitter = new SrsRtmpJitter();
... ... @@ -1148,7 +1149,6 @@ int SrsHls::on_publish(SrsRequest* req)
std::string stream = req->stream;
std::string app = req->app;
// TODO: support reload.
if (!config->get_hls_enabled(vhost)) {
return ret;
}
... ... @@ -1156,30 +1156,11 @@ int SrsHls::on_publish(SrsRequest* req)
// if enabled, open the muxer.
hls_enabled = true;
// TODO: subscribe the reload event.
int hls_fragment = 0;
int hls_window = 0;
SrsConfDirective* conf = NULL;
if ((conf = config->get_hls_fragment(vhost)) != NULL && !conf->arg0().empty()) {
hls_fragment = ::atoi(conf->arg0().c_str());
}
if (hls_fragment <= 0) {
hls_fragment = SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
if ((conf = config->get_hls_window(vhost)) != NULL && !conf->arg0().empty()) {
hls_window = ::atoi(conf->arg0().c_str());
}
if (hls_window <= 0) {
hls_window = SRS_CONF_DEFAULT_HLS_WINDOW;
}
int hls_fragment = config->get_hls_fragment(vhost);
int hls_window = config->get_hls_window(vhost);
// get the hls path config
std::string hls_path = SRS_CONF_DEFAULT_HLS_PATH;
if ((conf = config->get_hls_path(vhost)) != NULL) {
hls_path = conf->arg0();
}
std::string hls_path = config->get_hls_path(vhost);
// open muxer
if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {
... ... @@ -1191,6 +1172,12 @@ int SrsHls::on_publish(SrsRequest* req)
srs_error("m3u8 muxer open segment failed. ret=%d", ret);
return ret;
}
// notice the source to get the cached sequence header.
if ((ret = source->on_hls_start()) != ERROR_SUCCESS) {
srs_error("callback source hls start failed. ret=%d", ret);
return ret;
}
return ret;
}
... ... @@ -1215,16 +1202,16 @@ void SrsHls::on_unpublish()
hls_enabled = false;
}
int SrsHls::on_meta_data(SrsOnMetaDataPacket* metadata)
int SrsHls::on_meta_data(SrsAmf0Object* metadata)
{
int ret = ERROR_SUCCESS;
if (!metadata || !metadata->metadata) {
if (!metadata) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
}
SrsAmf0Object* obj = metadata->metadata;
SrsAmf0Object* obj = metadata;
if (obj->size() <= 0) {
srs_trace("no metadata persent, hls ignored it.");
return ret;
... ... @@ -1273,7 +1260,6 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio)
SrsAutoFree(SrsSharedPtrMessage, audio, false);
// TODO: maybe donot need to demux the aac?
if (!hls_enabled) {
return ret;
}
... ... @@ -1293,14 +1279,13 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio)
return ret;
}
int64_t corrected_time = 0;
if ((ret = jitter->correct(audio, 0, 0, &corrected_time)) != ERROR_SUCCESS) {
if ((ret = jitter->correct(audio, 0, 0)) != ERROR_SUCCESS) {
srs_error("rtmp jitter correct audio failed. ret=%d", ret);
return ret;
}
// the pts calc from rtmp/flv header.
int64_t pts = corrected_time * 90;
int64_t pts = audio->header.timestamp * 90;
if ((ret = ts_cache->write_audio(codec, muxer, pts, sample)) != ERROR_SUCCESS) {
srs_error("ts cache write audio failed. ret=%d", ret);
... ... @@ -1316,7 +1301,6 @@ int SrsHls::on_video(SrsSharedPtrMessage* video)
SrsAutoFree(SrsSharedPtrMessage, video, false);
// TODO: maybe donot need to demux the avc?
if (!hls_enabled) {
return ret;
}
... ... @@ -1337,24 +1321,23 @@ int SrsHls::on_video(SrsSharedPtrMessage* video)
return ret;
}
int64_t corrected_time = 0;
if ((ret = jitter->correct(video, 0, 0, &corrected_time)) != ERROR_SUCCESS) {
if ((ret = jitter->correct(video, 0, 0)) != ERROR_SUCCESS) {
srs_error("rtmp jitter correct video failed. ret=%d", ret);
return ret;
}
int64_t dts = corrected_time * 90;
int64_t dts = video->header.timestamp * 90;
if ((ret = ts_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {
srs_error("ts cache write video failed. ret=%d", ret);
return ret;
}
_mpegts();
hls_mux();
return ret;
}
void SrsHls::_mpegts()
void SrsHls::hls_mux()
{
// reportable
if (pithy_print->can_print()) {
... ...
... ... @@ -34,16 +34,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <vector>
class SrsOnMetaDataPacket;
class SrsSharedPtrMessage;
class SrsCodecSample;
class SrsCodecBuffer;
class SrsMpegtsFrame;
class SrsAmf0Object;
class SrsRtmpJitter;
class SrsTSMuxer;
class SrsCodec;
class SrsRequest;
class SrsPithyPrint;
class SrsSource;
/**
* jitter correct for audio,
... ... @@ -207,21 +208,39 @@ private:
SrsTSCache* ts_cache;
private:
bool hls_enabled;
SrsSource* source;
SrsCodec* codec;
SrsCodecSample* sample;
SrsRtmpJitter* jitter;
SrsPithyPrint* pithy_print;
public:
SrsHls();
SrsHls(SrsSource* _source);
virtual ~SrsHls();
public:
/**
* publish stream event, continue to write the m3u8,
* for the muxer object not destroyed.
*/
virtual int on_publish(SrsRequest* req);
/**
* the unpublish event, only close the muxer, donot destroy the
* muxer, for when we continue to publish, the m3u8 will continue.
*/
virtual void on_unpublish();
virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
/**
* get some information from metadata, it's optinal.
*/
virtual int on_meta_data(SrsAmf0Object* metadata);
/**
* mux the audio packets to ts.
*/
virtual int on_audio(SrsSharedPtrMessage* audio);
/**
* mux the video packets to ts.
*/
virtual int on_video(SrsSharedPtrMessage* video);
private:
virtual void _mpegts();
virtual void hls_mux();
};
#endif
... ...
... ... @@ -184,15 +184,7 @@ void SrsHttpClient::disconnect()
{
connected = false;
if (stfd) {
int fd = st_netfd_fileno(stfd);
st_netfd_close(stfd);
stfd = NULL;
// st does not close it sometimes,
// close it manually.
::close(fd);
}
srs_close_stfd(stfd);
}
int SrsHttpClient::connect(SrsHttpUri* uri)
... ...
... ... @@ -36,7 +36,6 @@ class SrsSocket;
#include <string>
#include <st.h>
#include <http_parser.h>
#define SRS_HTTP_HEADER_BUFFER 1024
... ...
... ... @@ -29,8 +29,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <map>
#include <st.h>
ILogContext::ILogContext()
{
}
... ...
... ... @@ -32,13 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_error.hpp>
#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200
#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300
#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100
#define SRS_STAGE_FORWARDER_INTERVAL_MS 2000
#define SRS_STAGE_ENCODER_INTERVAL_MS 2000
#define SRS_STAGE_HLS_INTERVAL_MS 2000
struct SrsStageInfo : public SrsReloadHandler
struct SrsStageInfo : public ISrsReloadHandler
{
int stage_id;
int pithy_print_time_ms;
... ... @@ -61,43 +56,23 @@ struct SrsStageInfo : public SrsReloadHandler
{
switch (stage_id) {
case SRS_STAGE_PLAY_USER: {
pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS;
SrsConfDirective* conf = config->get_pithy_print_play();
if (conf && !conf->arg0().empty()) {
pithy_print_time_ms = ::atoi(conf->arg0().c_str());
}
pithy_print_time_ms = config->get_pithy_print_play();
break;
}
case SRS_STAGE_PUBLISH_USER: {
pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
SrsConfDirective* conf = config->get_pithy_print_publish();
if (conf && !conf->arg0().empty()) {
pithy_print_time_ms = ::atoi(conf->arg0().c_str());
}
pithy_print_time_ms = config->get_pithy_print_publish();
break;
}
case SRS_STAGE_FORWARDER: {
pithy_print_time_ms = SRS_STAGE_FORWARDER_INTERVAL_MS;
SrsConfDirective* conf = config->get_pithy_print_forwarder();
if (conf && !conf->arg0().empty()) {
pithy_print_time_ms = ::atoi(conf->arg0().c_str());
}
pithy_print_time_ms = config->get_pithy_print_forwarder();
break;
}
case SRS_STAGE_ENCODER: {
pithy_print_time_ms = SRS_STAGE_ENCODER_INTERVAL_MS;
SrsConfDirective* conf = config->get_pithy_print_encoder();
if (conf && !conf->arg0().empty()) {
pithy_print_time_ms = ::atoi(conf->arg0().c_str());
}
pithy_print_time_ms = config->get_pithy_print_encoder();
break;
}
case SRS_STAGE_HLS: {
pithy_print_time_ms = SRS_STAGE_HLS_INTERVAL_MS;
SrsConfDirective* conf = config->get_pithy_print_hls();
if (conf && !conf->arg0().empty()) {
pithy_print_time_ms = ::atoi(conf->arg0().c_str());
}
pithy_print_time_ms = config->get_pithy_print_hls();
break;
}
default: {
... ...
... ... @@ -307,6 +307,11 @@ void SrsProtocol::set_send_timeout(int64_t timeout_us)
return skt->set_send_timeout(timeout_us);
}
int64_t SrsProtocol::get_send_timeout()
{
return skt->get_send_timeout();
}
int64_t SrsProtocol::get_recv_bytes()
{
return skt->get_recv_bytes();
... ... @@ -349,7 +354,7 @@ int SrsProtocol::recv_message(SrsCommonMessage** pmsg)
}
if (msg->size <= 0 || msg->header.payload_length <= 0) {
srs_trace("ignore empty message(type=%d, size=%d, time=%d, sid=%d).",
srs_trace("ignore empty message(type=%d, size=%d, time=%"PRId64", sid=%d).",
msg->header.message_type, msg->header.payload_length,
msg->header.timestamp, msg->header.stream_id);
srs_freep(msg);
... ... @@ -400,12 +405,13 @@ int SrsProtocol::send_message(ISrsMessage* msg)
// chunk message header, 11 bytes
// timestamp, 3bytes, big-endian
if (msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP) {
u_int32_t timestamp = (u_int32_t)msg->header.timestamp;
if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
*pheader++ = 0xFF;
*pheader++ = 0xFF;
*pheader++ = 0xFF;
} else {
pp = (char*)&msg->header.timestamp;
pp = (char*)&timestamp;
*pheader++ = pp[2];
*pheader++ = pp[1];
*pheader++ = pp[0];
... ... @@ -428,8 +434,8 @@ int SrsProtocol::send_message(ISrsMessage* msg)
*pheader++ = pp[3];
// chunk extended timestamp header, 0 or 4 bytes, big-endian
if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){
pp = (char*)&msg->header.timestamp;
if(timestamp >= RTMP_EXTENDED_TIMESTAMP){
pp = (char*)&timestamp;
*pheader++ = pp[3];
*pheader++ = pp[2];
*pheader++ = pp[1];
... ... @@ -456,8 +462,9 @@ int SrsProtocol::send_message(ISrsMessage* msg)
// must send the extended-timestamp to flash-player.
// @see: ngx_rtmp_prepare_message
// @see: http://blog.csdn.net/win_lin/article/details/13363699
if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){
pp = (char*)&msg->header.timestamp;
u_int32_t timestamp = (u_int32_t)msg->header.timestamp;
if(timestamp >= RTMP_EXTENDED_TIMESTAMP){
pp = (char*)&timestamp;
*pheader++ = pp[3];
*pheader++ = pp[2];
*pheader++ = pp[1];
... ... @@ -697,7 +704,7 @@ int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid);
} else {
chunk = chunk_streams[cid];
srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)",
srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length,
chunk->header.timestamp, chunk->header.stream_id);
}
... ... @@ -711,7 +718,7 @@ int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
return ret;
}
srs_verbose("read message header success. "
"fmt=%d, mh_size=%d, ext_time=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)",
"fmt=%d, mh_size=%d, ext_time=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
fmt, mh_size, chunk->extended_timestamp, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type,
chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);
... ... @@ -733,14 +740,14 @@ int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
// not got an entire RTMP message, try next chunk.
if (!msg) {
srs_verbose("get partial message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)",
srs_verbose("get partial message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length,
chunk->header.timestamp, chunk->header.stream_id);
return ret;
}
*pmsg = msg;
srs_info("get entire message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)",
srs_info("get entire message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length,
chunk->header.timestamp, chunk->header.stream_id);
... ... @@ -947,16 +954,16 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz
pp[1] = *p++;
pp[2] = *p++;
pp[3] = *p++;
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d, sid=%d",
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64", payload=%d, type=%d, sid=%d",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length,
chunk->header.message_type, chunk->header.stream_id);
} else {
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d",
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64", payload=%d, type=%d",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length,
chunk->header.message_type);
}
} else {
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d",
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64"",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp);
}
} else {
... ... @@ -981,7 +988,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz
// ffmpeg/librtmp may donot send this filed, need to detect the value.
// @see also: http://blog.csdn.net/win_lin/article/details/13363699
int32_t timestamp = 0x00;
u_int32_t timestamp = 0x00;
char* pp = (char*)&timestamp;
pp[3] = *p++;
pp[2] = *p++;
... ... @@ -990,14 +997,14 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz
// compare to the chunk timestamp, which is set by chunk message header
// type 0,1 or 2.
int32_t chunk_timestamp = chunk->header.timestamp;
u_int32_t chunk_timestamp = chunk->header.timestamp;
if (chunk_timestamp > RTMP_EXTENDED_TIMESTAMP && chunk_timestamp != timestamp) {
mh_size -= 4;
srs_verbose("ignore the 4bytes extended timestamp. mh_size=%d", mh_size);
} else {
chunk->header.timestamp = timestamp;
}
srs_verbose("header read ext_time completed. time=%d", chunk->header.timestamp);
srs_verbose("header read ext_time completed. time=%"PRId64"", chunk->header.timestamp);
}
// valid message
... ... @@ -1027,7 +1034,7 @@ int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh
buffer->erase(bh_size + mh_size);
srs_trace("get an empty RTMP "
"message(type=%d, size=%d, time=%d, sid=%d)", chunk->header.message_type,
"message(type=%d, size=%d, time=%"PRId64", sid=%d)", chunk->header.message_type,
chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);
*pmsg = chunk->msg;
... ... @@ -1068,13 +1075,13 @@ int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh
if (chunk->header.payload_length == chunk->msg->size) {
*pmsg = chunk->msg;
chunk->msg = NULL;
srs_verbose("get entire RTMP message(type=%d, size=%d, time=%d, sid=%d)",
srs_verbose("get entire RTMP message(type=%d, size=%d, time=%"PRId64", sid=%d)",
chunk->header.message_type, chunk->header.payload_length,
chunk->header.timestamp, chunk->header.stream_id);
return ret;
}
srs_verbose("get partial RTMP message(type=%d, size=%d, time=%d, sid=%d), partial size=%d",
srs_verbose("get partial RTMP message(type=%d, size=%d, time=%"PRId64", sid=%d), partial size=%d",
chunk->header.message_type, chunk->header.payload_length,
chunk->header.timestamp, chunk->header.stream_id,
chunk->msg->size);
... ...
... ... @@ -33,8 +33,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <map>
#include <string>
#include <st.h>
#include <srs_core_log.hpp>
#include <srs_core_error.hpp>
... ... @@ -117,6 +115,7 @@ public:
virtual void set_recv_timeout(int64_t timeout_us);
virtual int64_t get_recv_timeout();
virtual void set_send_timeout(int64_t timeout_us);
virtual int64_t get_send_timeout();
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
virtual int get_recv_kbps();
... ... @@ -205,8 +204,9 @@ struct SrsMessageHeader
* Four-byte field that contains a timestamp of the message.
* The 4 bytes are packed in the big-endian order.
* @remark, used as calc timestamp when decode and encode time.
* @remark, we use 64bits for large time for jitter detect and hls.
*/
u_int32_t timestamp;
int64_t timestamp;
SrsMessageHeader();
virtual ~SrsMessageHeader();
... ... @@ -1156,7 +1156,7 @@ int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T**
T* pkt = dynamic_cast<T*>(msg->get_packet());
if (!pkt) {
delete msg;
srs_trace("drop message(type=%d, size=%d, time=%d, sid=%d).",
srs_trace("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).",
msg->header.message_type, msg->header.payload_length,
msg->header.timestamp, msg->header.stream_id);
continue;
... ...
... ... @@ -23,22 +23,54 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_reload.hpp>
using namespace std;
#include <srs_core_error.hpp>
SrsReloadHandler::SrsReloadHandler()
ISrsReloadHandler::ISrsReloadHandler()
{
}
ISrsReloadHandler::~ISrsReloadHandler()
{
}
SrsReloadHandler::~SrsReloadHandler()
int ISrsReloadHandler::on_reload_listen()
{
return ERROR_SUCCESS;
}
int ISrsReloadHandler::on_reload_pithy_print()
{
return ERROR_SUCCESS;
}
int ISrsReloadHandler::on_reload_vhost_removed(string /*vhost*/)
{
return ERROR_SUCCESS;
}
int ISrsReloadHandler::on_reload_gop_cache(string /*vhost*/)
{
return ERROR_SUCCESS;
}
int ISrsReloadHandler::on_reload_queue_length(string /*vhost*/)
{
return ERROR_SUCCESS;
}
int ISrsReloadHandler::on_reload_forward(string /*vhost*/)
{
return ERROR_SUCCESS;
}
int SrsReloadHandler::on_reload_listen()
int ISrsReloadHandler::on_reload_hls(string /*vhost*/)
{
return ERROR_SUCCESS;
}
int SrsReloadHandler::on_reload_pithy_print()
int ISrsReloadHandler::on_reload_transcode(string /*vhost*/)
{
return ERROR_SUCCESS;
}
... ...
... ... @@ -29,17 +29,25 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core.hpp>
#include <string>
/**
* the handler for config reload.
*/
class SrsReloadHandler
class ISrsReloadHandler
{
public:
SrsReloadHandler();
virtual ~SrsReloadHandler();
ISrsReloadHandler();
virtual ~ISrsReloadHandler();
public:
virtual int on_reload_listen();
virtual int on_reload_pithy_print();
virtual int on_reload_vhost_removed(std::string vhost);
virtual int on_reload_gop_cache(std::string vhost);
virtual int on_reload_queue_length(std::string vhost);
virtual int on_reload_forward(std::string vhost);
virtual int on_reload_hls(std::string vhost);
virtual int on_reload_transcode(std::string vhost);
};
#endif
\ No newline at end of file
... ...
... ... @@ -33,6 +33,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_handshake.hpp>
#include <srs_core_config.hpp>
using namespace std;
/**
* the signature for packets to client.
*/
... ... @@ -79,6 +81,23 @@ SrsRequest::~SrsRequest()
{
}
SrsRequest* SrsRequest::copy()
{
SrsRequest* cp = new SrsRequest();
cp->app = app;
cp->objectEncoding = objectEncoding;
cp->pageUrl = pageUrl;
cp->port = port;
cp->schema = schema;
cp->stream = stream;
cp->swfUrl = swfUrl;
cp->tcUrl = tcUrl;
cp->vhost = vhost;
return cp;
}
int SrsRequest::discovery_app()
{
int ret = ERROR_SUCCESS;
... ... @@ -125,6 +144,8 @@ int SrsRequest::discovery_app()
if (parsed_vhost) {
vhost = parsed_vhost->arg0();
}
// TODO: discovery the params of vhost.
srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
schema.c_str(), vhost.c_str(), port.c_str(), app.c_str());
... ... @@ -142,7 +163,7 @@ int SrsRequest::discovery_app()
return ret;
}
std::string SrsRequest::get_stream_url()
string SrsRequest::get_stream_url()
{
std::string url = "";
... ... @@ -162,7 +183,7 @@ void SrsRequest::strip()
trim(stream, "/ \n\r\t");
}
std::string& SrsRequest::trim(std::string& str, std::string chs)
std::string& SrsRequest::trim(string& str, string chs)
{
for (int i = 0; i < (int)chs.length(); i++) {
char ch = chs.at(i);
... ... @@ -245,6 +266,9 @@ int SrsRtmpClient::handshake()
SrsSocket skt(stfd);
skt.set_recv_timeout(protocol->get_recv_timeout());
skt.set_send_timeout(protocol->get_send_timeout());
SrsComplexHandshake complex_hs;
SrsSimpleHandshake simple_hs;
if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) {
... ... @@ -436,6 +460,11 @@ void SrsRtmp::set_send_timeout(int64_t timeout_us)
protocol->set_send_timeout(timeout_us);
}
int64_t SrsRtmp::get_send_timeout()
{
return protocol->get_send_timeout();
}
int64_t SrsRtmp::get_recv_bytes()
{
return protocol->get_recv_bytes();
... ... @@ -472,6 +501,9 @@ int SrsRtmp::handshake()
SrsSocket skt(stfd);
skt.set_recv_timeout(protocol->get_recv_timeout());
skt.set_send_timeout(protocol->get_send_timeout());
SrsComplexHandshake complex_hs;
SrsSimpleHandshake simple_hs;
if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) {
... ... @@ -1127,7 +1159,7 @@ int SrsRtmp::start_bandwidth_check(int max_play_kbps, int max_pub_kbps)
return ret;
}
int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name)
int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, string& stream_name)
{
int ret = ERROR_SUCCESS;
... ... @@ -1184,7 +1216,7 @@ int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int strea
return ret;
}
int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name)
int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, string& stream_name)
{
int ret = ERROR_SUCCESS;
... ... @@ -1208,7 +1240,7 @@ int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType
return ret;
}
int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name)
int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, string& stream_name)
{
int ret = ERROR_SUCCESS;
... ...
... ... @@ -33,8 +33,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <st.h>
class SrsProtocol;
class ISrsMessage;
class SrsCommonMessage;
... ... @@ -69,6 +67,13 @@ struct SrsRequest
SrsRequest();
virtual ~SrsRequest();
/**
* deep copy the request, for source to use it to support reload,
* for when initialize the source, the request is valid,
* when reload it, the request maybe invalid, so need to copy it.
*/
virtual SrsRequest* copy();
/**
* disconvery vhost/app from tcUrl.
... ... @@ -148,6 +153,7 @@ public:
virtual void set_recv_timeout(int64_t timeout_us);
virtual int64_t get_recv_timeout();
virtual void set_send_timeout(int64_t timeout_us);
virtual int64_t get_send_timeout();
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
virtual int get_recv_kbps();
... ...
... ... @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <algorithm>
#include <st.h>
#include <srs_core_log.hpp>
#include <srs_core_error.hpp>
#include <srs_core_client.hpp>
... ... @@ -48,24 +46,16 @@ SrsListener::SrsListener(SrsServer* _server, SrsListenerType _type)
port = 0;
server = _server;
type = _type;
tid = NULL;
loop = false;
pthread = new SrsThread(this, 0);
}
SrsListener::~SrsListener()
{
if (stfd) {
st_netfd_close(stfd);
stfd = NULL;
}
srs_close_stfd(stfd);
if (tid) {
loop = false;
st_thread_interrupt(tid);
st_thread_join(tid, NULL);
tid = NULL;
}
pthread->stop();
srs_freep(pthread);
// st does not close it sometimes,
// close it manually.
... ... @@ -118,8 +108,7 @@ int SrsListener::listen(int _port)
}
srs_verbose("st open socket success. fd=%d", fd);
if ((tid = st_thread_create(listen_thread, this, 1, 0)) == NULL) {
ret = ERROR_ST_CREATE_LISTEN_THREAD;
if ((ret = pthread->start()) != ERROR_SUCCESS) {
srs_error("st_thread_create listen thread error. ret=%d", ret);
return ret;
}
... ... @@ -130,41 +119,32 @@ int SrsListener::listen(int _port)
return ret;
}
void SrsListener::listen_cycle()
void SrsListener::on_enter_loop()
{
int ret = ERROR_SUCCESS;
log_context->generate_id();
srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd);
while (loop) {
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
if(client_stfd == NULL){
// ignore error.
srs_warn("ignore accept thread stoppped for accept client error");
continue;
}
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
continue;
}
srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
}
}
void* SrsListener::listen_thread(void* arg)
int SrsListener::cycle()
{
SrsListener* obj = (SrsListener*)arg;
srs_assert(obj != NULL);
int ret = ERROR_SUCCESS;
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
if(client_stfd == NULL){
// ignore error.
srs_warn("ignore accept thread stoppped for accept client error");
return ret;
}
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
obj->loop = true;
obj->listen_cycle();
if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
return ret;
}
return NULL;
srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
return ret;
}
SrsServer::SrsServer()
... ... @@ -312,8 +292,7 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
srs_error("exceed the max connections, drop client: "
"clients=%d, max=%d, fd=%d", (int)conns.size(), max_connections, fd);
st_netfd_close(client_stfd);
::close(fd);
srs_close_stfd(client_stfd);
return ret;
}
... ...
... ... @@ -32,9 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <vector>
#include <st.h>
#include <srs_core_reload.hpp>
#include <srs_core_thread.hpp>
class SrsServer;
class SrsConnection;
... ... @@ -45,7 +44,7 @@ enum SrsListenerType
SrsListenerApi
};
class SrsListener
class SrsListener : public ISrsThreadHandler
{
public:
SrsListenerType type;
... ... @@ -54,19 +53,19 @@ private:
st_netfd_t stfd;
int port;
SrsServer* server;
st_thread_t tid;
bool loop;
SrsThread* pthread;
public:
SrsListener(SrsServer* _server, SrsListenerType _type);
virtual ~SrsListener();
public:
virtual int listen(int port);
private:
virtual void listen_cycle();
static void* listen_thread(void* arg);
// interface ISrsThreadHandler.
public:
virtual void on_enter_loop();
virtual int cycle();
};
class SrsServer : public SrsReloadHandler
class SrsServer : public ISrsReloadHandler
{
friend class SrsListener;
private:
... ...
... ... @@ -52,6 +52,11 @@ void SrsSocket::set_send_timeout(int64_t timeout_us)
send_timeout = timeout_us;
}
int64_t SrsSocket::get_send_timeout()
{
return send_timeout;
}
int64_t SrsSocket::get_recv_bytes()
{
return recv_bytes;
... ...
... ... @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#include <st.h>
/**
* the socket provides TCP socket over st,
* that is, the sync socket mechanism.
... ... @@ -52,6 +50,7 @@ public:
virtual void set_recv_timeout(int64_t timeout_us);
virtual int64_t get_recv_timeout();
virtual void set_send_timeout(int64_t timeout_us);
virtual int64_t get_send_timeout();
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
virtual int get_recv_kbps();
... ...
... ... @@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_source.hpp>
#include <algorithm>
using namespace std;
#include <srs_core_log.hpp>
#include <srs_core_protocol.hpp>
... ... @@ -37,8 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_rtmp.hpp>
#define CONST_MAX_JITTER_MS 500
#define DEFAULT_FRAME_TIME_MS 10
#define PAUSED_SHRINK_SIZE 250
#define DEFAULT_FRAME_TIME_MS 40
SrsRtmpJitter::SrsRtmpJitter()
{
... ... @@ -49,9 +49,15 @@ SrsRtmpJitter::~SrsRtmpJitter()
{
}
int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t* corrected_time)
int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv)
{
int ret = ERROR_SUCCESS;
// set to 0 for metadata.
if (!msg->header.is_video() && !msg->header.is_audio()) {
msg->header.timestamp = 0;
return ret;
}
int sample_rate = tba;
int frame_rate = tbv;
... ... @@ -66,16 +72,16 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t*
* 3. last_pkt_correct_time: simply add the positive delta,
* and enforce the time monotonically.
*/
u_int32_t time = msg->header.timestamp;
int32_t delta = time - last_pkt_time;
int64_t time = msg->header.timestamp;
int64_t delta = time - last_pkt_time;
// if jitter detected, reset the delta.
if (delta < 0 || delta > CONST_MAX_JITTER_MS) {
// calc the right diff by audio sample rate
if (msg->header.is_audio() && sample_rate > 0) {
delta = (int32_t)(delta * 1000.0 / sample_rate);
delta = (int64_t)(delta * 1000.0 / sample_rate);
} else if (msg->header.is_video() && frame_rate > 0) {
delta = (int32_t)(delta * 1.0 / frame_rate);
delta = (int64_t)(delta * 1.0 / frame_rate);
} else {
delta = DEFAULT_FRAME_TIME_MS;
}
... ... @@ -85,20 +91,16 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t*
delta = DEFAULT_FRAME_TIME_MS;
}
srs_info("jitter detected, last_pts=%d, pts=%d, diff=%d, last_time=%d, time=%d, diff=%d",
srs_info("jitter detected, last_pts=%"PRId64", pts=%"PRId64", diff=%"PRId64", last_time=%"PRId64", time=%"PRId64", diff=%"PRId64"",
last_pkt_time, time, time - last_pkt_time, last_pkt_correct_time, last_pkt_correct_time + delta, delta);
} else {
srs_verbose("timestamp no jitter. time=%d, last_pkt=%d, correct_to=%d",
srs_verbose("timestamp no jitter. time=%"PRId64", last_pkt=%"PRId64", correct_to=%"PRId64"",
time, last_pkt_time, last_pkt_correct_time + delta);
}
last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta);
if (corrected_time) {
*corrected_time = last_pkt_correct_time;
}
msg->header.timestamp = last_pkt_correct_time;
last_pkt_time = time;
return ret;
... ... @@ -109,61 +111,60 @@ int SrsRtmpJitter::get_time()
return (int)last_pkt_correct_time;
}
SrsConsumer::SrsConsumer(SrsSource* _source)
SrsMessageQueue::SrsMessageQueue()
{
source = _source;
paused = false;
jitter = new SrsRtmpJitter();
queue_size_ms = 0;
av_start_time = av_end_time = -1;
}
SrsConsumer::~SrsConsumer()
SrsMessageQueue::~SrsMessageQueue()
{
clear();
source->on_consumer_destroy(this);
srs_freep(jitter);
}
int SrsConsumer::get_time()
void SrsMessageQueue::set_queue_size(double queue_size)
{
return jitter->get_time();
queue_size_ms = (int)(queue_size * 1000);
}
int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int tba, int tbv)
int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg)
{
int ret = ERROR_SUCCESS;
if ((ret = jitter->correct(msg, tba, tbv)) != ERROR_SUCCESS) {
srs_freep(msg);
return ret;
if (msg->header.is_video() || msg->header.is_audio()) {
if (av_start_time == -1) {
av_start_time = msg->header.timestamp;
}
av_end_time = msg->header.timestamp;
}
// TODO: check the queue size and drop packets if overflow.
msgs.push_back(msg);
while (av_end_time - av_start_time > queue_size_ms) {
shrink();
}
return ret;
}
int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count)
int SrsMessageQueue::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count)
{
int ret = ERROR_SUCCESS;
if (msgs.empty()) {
return ret;
}
if (paused) {
if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) {
shrink();
}
return ret;
}
if (max_count == 0) {
count = (int)msgs.size();
} else {
count = srs_min(max_count, (int)msgs.size());
}
if (count <= 0) {
return ret;
}
pmsgs = new SrsSharedPtrMessage*[count];
... ... @@ -171,6 +172,9 @@ int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& c
pmsgs[i] = msgs[i];
}
SrsSharedPtrMessage* last = msgs[count - 1];
av_start_time = last->header.timestamp;
if (count == (int)msgs.size()) {
msgs.clear();
} else {
... ... @@ -180,76 +184,120 @@ int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& c
return ret;
}
int SrsConsumer::on_play_client_pause(bool is_pause)
void SrsMessageQueue::shrink()
{
int ret = ERROR_SUCCESS;
srs_trace("stream consumer change pause state %d=>%d", paused, is_pause);
paused = is_pause;
return ret;
}
void SrsConsumer::shrink()
{
int i = 0;
std::vector<SrsSharedPtrMessage*>::iterator it;
// issue the last video iframe.
bool has_video = false;
int frame_to_remove = 0;
std::vector<SrsSharedPtrMessage*>::iterator iframe = msgs.end();
for (i = 0, it = msgs.begin(); it != msgs.end(); ++it, i++) {
SrsSharedPtrMessage* msg = *it;
int iframe_index = -1;
// issue the first iframe.
// skip the first frame, whatever the type of it,
// for when we shrinked, the first is the iframe,
// we will directly remove the gop next time.
for (int i = 1; i < (int)msgs.size(); i++) {
SrsSharedPtrMessage* msg = msgs[i];
if (msg->header.is_video()) {
has_video = true;
if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) {
iframe = it;
frame_to_remove = i + 1;
// the max frame index to remove.
iframe_index = i;
// set the start time, we will remove until this frame.
av_start_time = msg->header.timestamp;
break;
}
}
}
// last iframe is the first elem, ignore it.
if (iframe == msgs.begin()) {
return;
}
// recalc the frame to remove
if (iframe == msgs.end()) {
frame_to_remove = 0;
}
if (!has_video) {
frame_to_remove = (int)msgs.size();
}
srs_trace("shrink the cache queue, has_video=%d, has_iframe=%d, size=%d, removed=%d",
has_video, iframe != msgs.end(), (int)msgs.size(), frame_to_remove);
// if no video, remove all audio.
if (!has_video) {
// no iframe, clear the queue.
if (iframe_index < 0) {
clear();
return;
}
// if exists video Iframe, remove the frames before it.
if (iframe != msgs.end()) {
for (it = msgs.begin(); it != iframe; ++it) {
SrsSharedPtrMessage* msg = *it;
srs_freep(msg);
}
msgs.erase(msgs.begin(), iframe);
srs_trace("shrink the cache queue, size=%d, removed=%d, max=%.2f",
(int)msgs.size(), iframe_index, queue_size_ms / 1000.0);
// remove the first gop from the front
for (int i = 0; i < iframe_index; i++) {
SrsSharedPtrMessage* msg = msgs[i];
srs_freep(msg);
}
msgs.erase(msgs.begin(), msgs.begin() + iframe_index);
}
void SrsConsumer::clear()
void SrsMessageQueue::clear()
{
std::vector<SrsSharedPtrMessage*>::iterator it;
for (it = msgs.begin(); it != msgs.end(); ++it) {
SrsSharedPtrMessage* msg = *it;
srs_freep(msg);
}
msgs.clear();
av_start_time = av_end_time = -1;
}
SrsConsumer::SrsConsumer(SrsSource* _source)
{
source = _source;
paused = false;
jitter = new SrsRtmpJitter();
queue = new SrsMessageQueue();
}
SrsConsumer::~SrsConsumer()
{
source->on_consumer_destroy(this);
srs_freep(jitter);
srs_freep(queue);
}
void SrsConsumer::set_queue_size(double queue_size)
{
queue->set_queue_size(queue_size);
}
int SrsConsumer::get_time()
{
return jitter->get_time();
}
int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int tba, int tbv)
{
int ret = ERROR_SUCCESS;
if ((ret = jitter->correct(msg, tba, tbv)) != ERROR_SUCCESS) {
srs_freep(msg);
return ret;
}
if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count)
{
// paused, return nothing.
if (paused) {
return ERROR_SUCCESS;
}
return queue->get_packets(max_count, pmsgs, count);
}
int SrsConsumer::on_play_client_pause(bool is_pause)
{
int ret = ERROR_SUCCESS;
srs_trace("stream consumer change pause state %d=>%d", paused, is_pause);
paused = is_pause;
return ret;
}
SrsGopCache::SrsGopCache()
... ... @@ -344,22 +392,25 @@ int SrsGopCache::dump(SrsConsumer* consumer, int tba, int tbv)
std::map<std::string, SrsSource*> SrsSource::pool;
SrsSource* SrsSource::find(const std::string &stream_url)
SrsSource* SrsSource::find(SrsRequest* req)
{
string stream_url = req->get_stream_url();
string vhost = req->vhost;
if (pool.find(stream_url) == pool.end()) {
pool[stream_url] = new SrsSource(stream_url);
srs_verbose("create new source for url=%s", stream_url.c_str());
pool[stream_url] = new SrsSource(req);
srs_verbose("create new source for url=%s, vhost=%s", stream_url.c_str(), vhost.c_str());
}
return pool[stream_url];
}
SrsSource::SrsSource(std::string _stream_url)
SrsSource::SrsSource(SrsRequest* _req)
{
stream_url = _stream_url;
req = _req->copy();
#ifdef SRS_HLS
hls = new SrsHls();
hls = new SrsHls(this);
#endif
#ifdef SRS_FFMPEG
encoder = new SrsEncoder();
... ... @@ -371,10 +422,14 @@ SrsSource::SrsSource(std::string _stream_url)
_can_publish = true;
gop_cache = new SrsGopCache();
config->subscribe(this);
}
SrsSource::~SrsSource()
{
config->unsubscribe(this);
if (true) {
std::vector<SrsConsumer*>::iterator it;
for (it = consumers.begin(); it != consumers.end(); ++it) {
... ... @@ -405,6 +460,167 @@ SrsSource::~SrsSource()
#ifdef SRS_FFMPEG
srs_freep(encoder);
#endif
srs_freep(req);
}
int SrsSource::on_reload_gop_cache(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
// gop cache changed.
bool enabled_cache = config->get_gop_cache(vhost);
srs_trace("vhost %s gop_cache changed to %d, source url=%s",
vhost.c_str(), enabled_cache, req->get_stream_url().c_str());
set_cache(enabled_cache);
return ret;
}
int SrsSource::on_reload_queue_length(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
double queue_size = config->get_queue_length(req->vhost);
if (true) {
std::vector<SrsConsumer*>::iterator it;
for (it = consumers.begin(); it != consumers.end(); ++it) {
SrsConsumer* consumer = *it;
consumer->set_queue_size(queue_size);
}
srs_trace("consumers reload queue size success.");
}
if (true) {
std::vector<SrsForwarder*>::iterator it;
for (it = forwarders.begin(); it != forwarders.end(); ++it) {
SrsForwarder* forwarder = *it;
forwarder->set_queue_size(queue_size);
}
srs_trace("forwarders reload queue size success.");
}
return ret;
}
int SrsSource::on_reload_forward(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
// forwarders
destroy_forwarders();
if ((ret = create_forwarders()) != ERROR_SUCCESS) {
srs_error("create forwarders failed. ret=%d", ret);
return ret;
}
srs_trace("vhost %s forwarders reload success", vhost.c_str());
return ret;
}
int SrsSource::on_reload_hls(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
#ifdef SRS_HLS
hls->on_unpublish();
if ((ret = hls->on_publish(req)) != ERROR_SUCCESS) {
srs_error("hls publish failed. ret=%d", ret);
return ret;
}
srs_trace("vhost %s hls reload success", vhost.c_str());
#endif
return ret;
}
int SrsSource::on_reload_transcode(string vhost)
{
int ret = ERROR_SUCCESS;
if (req->vhost != vhost) {
return ret;
}
#ifdef SRS_FFMPEG
encoder->on_unpublish();
if ((ret = encoder->on_publish(req)) != ERROR_SUCCESS) {
srs_error("start encoder failed. ret=%d", ret);
return ret;
}
srs_trace("vhost %s transcode reload success", vhost.c_str());
#endif
return ret;
}
int SrsSource::on_forwarder_start(SrsForwarder* forwarder)
{
int ret = ERROR_SUCCESS;
// feed the forwarder the metadata/sequence header,
// when reload to enable the forwarder.
if (cache_metadata && (ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) {
srs_error("forwarder process onMetaData message failed. ret=%d", ret);
return ret;
}
if (cache_sh_video && (ret = forwarder->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) {
srs_error("forwarder process video sequence header message failed. ret=%d", ret);
return ret;
}
if (cache_sh_audio && (ret = forwarder->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) {
srs_error("forwarder process audio sequence header message failed. ret=%d", ret);
return ret;
}
return ret;
}
int SrsSource::on_hls_start()
{
int ret = ERROR_SUCCESS;
#ifdef SRS_HLS
// feed the hls the metadata/sequence header,
// when reload to enable the hls.
// TODO: maybe need to decode the metadata?
if (cache_sh_video && (ret = hls->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) {
srs_error("hls process video sequence header message failed. ret=%d", ret);
return ret;
}
if (cache_sh_audio && (ret = hls->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) {
srs_error("hls process audio sequence header message failed. ret=%d", ret);
return ret;
}
#endif
return ret;
}
bool SrsSource::can_publish()
... ... @@ -417,7 +633,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata
int ret = ERROR_SUCCESS;
#ifdef SRS_HLS
if ((ret = hls->on_meta_data(metadata)) != ERROR_SUCCESS) {
if (metadata && (ret = hls->on_meta_data(metadata->metadata)) != ERROR_SUCCESS) {
srs_error("hls process onMetaData message failed. ret=%d", ret);
return ret;
}
... ... @@ -425,6 +641,8 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata
metadata->metadata->set("server", new SrsAmf0String(
RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
metadata->metadata->set("contributor",
new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));
SrsAmf0Any* prop = NULL;
if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) {
... ... @@ -620,7 +838,7 @@ int SrsSource::on_video(SrsCommonMessage* video)
// cache the last gop packets
if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) {
srs_error("shrink gop cache failed. ret=%d", ret);
srs_error("gop cache msg failed. ret=%d", ret);
return ret;
}
srs_verbose("cache gop success.");
... ... @@ -628,39 +846,33 @@ int SrsSource::on_video(SrsCommonMessage* video)
return ret;
}
int SrsSource::on_publish(SrsRequest* req)
int SrsSource::on_publish(SrsRequest* _req)
{
int ret = ERROR_SUCCESS;
// update the request object.
srs_freep(req);
req = _req->copy();
srs_assert(req);
_can_publish = false;
// TODO: support reload.
// create forwarders
SrsConfDirective* conf = config->get_forward(req->vhost);
for (int i = 0; conf && i < (int)conf->args.size(); i++) {
std::string forward_server = conf->args.at(i);
SrsForwarder* forwarder = new SrsForwarder();
forwarders.push_back(forwarder);
if ((ret = forwarder->on_publish(req, forward_server)) != ERROR_SUCCESS) {
srs_error("start forwarder failed. "
"vhost=%s, app=%s, stream=%s, forward-to=%s",
req->vhost.c_str(), req->app.c_str(), req->stream.c_str(),
forward_server.c_str());
return ret;
}
if ((ret = create_forwarders()) != ERROR_SUCCESS) {
srs_error("create forwarders failed. ret=%d", ret);
return ret;
}
#ifdef SRS_FFMPEG
if ((ret = encoder->on_publish(req)) != ERROR_SUCCESS) {
srs_error("start encoder failed. ret=%d", ret);
return ret;
}
#endif
#ifdef SRS_HLS
if ((ret = hls->on_publish(req)) != ERROR_SUCCESS) {
srs_error("start hls failed. ret=%d", ret);
return ret;
}
#endif
... ... @@ -670,19 +882,14 @@ int SrsSource::on_publish(SrsRequest* req)
void SrsSource::on_unpublish()
{
// close all forwarders
std::vector<SrsForwarder*>::iterator it;
for (it = forwarders.begin(); it != forwarders.end(); ++it) {
SrsForwarder* forwarder = *it;
forwarder->on_unpublish();
srs_freep(forwarder);
}
forwarders.clear();
// destroy all forwarders
destroy_forwarders();
#ifdef SRS_FFMPEG
encoder->on_unpublish();
#endif
// TODO: HLS should continue previous sequence and stream.
#ifdef SRS_HLS
hls->on_unpublish();
#endif
... ... @@ -706,6 +913,9 @@ void SrsSource::on_unpublish()
consumer = new SrsConsumer(this);
consumers.push_back(consumer);
double queue_size = config->get_queue_length(req->vhost);
consumer->set_queue_size(queue_size);
if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) {
srs_error("dispatch metadata failed. ret=%d", ret);
... ... @@ -729,6 +939,8 @@ void SrsSource::on_unpublish()
return ret;
}
srs_trace("create consumer, queue_size=%.2f, tba=%d, tbv=%d", queue_size, sample_rate, frame_rate);
return ret;
}
... ... @@ -747,3 +959,40 @@ void SrsSource::set_cache(bool enabled)
gop_cache->set(enabled);
}
int SrsSource::create_forwarders()
{
int ret = ERROR_SUCCESS;
SrsConfDirective* conf = config->get_forward(req->vhost);
for (int i = 0; conf && i < (int)conf->args.size(); i++) {
std::string forward_server = conf->args.at(i);
SrsForwarder* forwarder = new SrsForwarder(this);
forwarders.push_back(forwarder);
double queue_size = config->get_queue_length(req->vhost);
forwarder->set_queue_size(queue_size);
if ((ret = forwarder->on_publish(req, forward_server)) != ERROR_SUCCESS) {
srs_error("start forwarder failed. "
"vhost=%s, app=%s, stream=%s, forward-to=%s",
req->vhost.c_str(), req->app.c_str(), req->stream.c_str(),
forward_server.c_str());
return ret;
}
}
return ret;
}
void SrsSource::destroy_forwarders()
{
std::vector<SrsForwarder*>::iterator it;
for (it = forwarders.begin(); it != forwarders.end(); ++it) {
SrsForwarder* forwarder = *it;
forwarder->on_unpublish();
srs_freep(forwarder);
}
forwarders.clear();
}
... ...
... ... @@ -34,6 +34,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <vector>
#include <string>
#include <srs_core_reload.hpp>
class SrsSource;
class SrsCommonMessage;
class SrsOnMetaDataPacket;
... ... @@ -54,18 +56,16 @@ class SrsEncoder;
class SrsRtmpJitter
{
private:
u_int32_t last_pkt_time;
u_int32_t last_pkt_correct_time;
int64_t last_pkt_time;
int64_t last_pkt_correct_time;
public:
SrsRtmpJitter();
virtual ~SrsRtmpJitter();
public:
/**
* detect the time jitter and correct it.
* @param corrected_time output the 64bits time.
* ignore if NULL.
*/
virtual int correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t* corrected_time = NULL);
virtual int correct(SrsSharedPtrMessage* msg, int tba, int tbv);
/**
* get current client time, the last packet time.
*/
... ... @@ -73,6 +73,48 @@ public:
};
/**
* the message queue for the consumer(client), forwarder.
* we limit the size in seconds, drop old messages(the whole gop) if full.
*/
class SrsMessageQueue
{
private:
int64_t av_start_time;
int64_t av_end_time;
int queue_size_ms;
std::vector<SrsSharedPtrMessage*> msgs;
public:
SrsMessageQueue();
virtual ~SrsMessageQueue();
public:
/**
* set the queue size
* @param queue_size the queue size in seconds.
*/
virtual void set_queue_size(double queue_size);
public:
/**
* enqueue the message, the timestamp always monotonically.
* @param msg, the msg to enqueue, user never free it whatever the return code.
*/
virtual int enqueue(SrsSharedPtrMessage* msg);
/**
* get packets in consumer queue.
* @pmsgs SrsMessages*[], output the prt array.
* @count the count in array.
* @max_count the max count to dequeue, 0 to dequeue all.
*/
virtual int get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count);
private:
/**
* remove a gop from the front.
* if no iframe found, clear it.
*/
virtual void shrink();
virtual void clear();
};
/**
* the consumer for SrsSource, that is a play client.
*/
class SrsConsumer
... ... @@ -80,12 +122,14 @@ class SrsConsumer
private:
SrsRtmpJitter* jitter;
SrsSource* source;
std::vector<SrsSharedPtrMessage*> msgs;
SrsMessageQueue* queue;
bool paused;
public:
SrsConsumer(SrsSource* _source);
virtual ~SrsConsumer();
public:
virtual void set_queue_size(double queue_size);
public:
/**
* get current client time, the last packet time.
*/
... ... @@ -109,13 +153,6 @@ public:
* when client send the pause message.
*/
virtual int on_play_client_pause(bool is_pause);
private:
/**
* when paused, shrink the cache queue,
* remove to cache only one gop.
*/
virtual void shrink();
virtual void clear();
};
/**
... ... @@ -158,20 +195,21 @@ public:
/**
* live streaming source.
*/
class SrsSource
class SrsSource : public ISrsReloadHandler
{
private:
static std::map<std::string, SrsSource*> pool;
public:
/**
* find stream by vhost/app/stream.
* @stream_url the stream url, for example, myserver.xxx.com/app/stream
* @param req the client request.
* @return the matched source, never be NULL.
* @remark stream_url should without port and schema.
*/
static SrsSource* find(const std::string& stream_url);
static SrsSource* find(SrsRequest* req);
private:
std::string stream_url;
// deep copy of client request.
SrsRequest* req;
// to delivery stream to clients.
std::vector<SrsConsumer*> consumers;
// hls handler.
... ... @@ -206,19 +244,43 @@ private:
// the cached audio sequence header.
SrsSharedPtrMessage* cache_sh_audio;
public:
SrsSource(std::string _stream_url);
/**
* @param _req the client request object,
* this object will deep copy it for reload.
*/
SrsSource(SrsRequest* _req);
virtual ~SrsSource();
// interface ISrsReloadHandler
public:
virtual int on_reload_gop_cache(std::string vhost);
virtual int on_reload_queue_length(std::string vhost);
virtual int on_reload_forward(std::string vhost);
virtual int on_reload_hls(std::string vhost);
virtual int on_reload_transcode(std::string vhost);
public:
// for the SrsForwarder to callback to request the sequence headers.
virtual int on_forwarder_start(SrsForwarder* forwarder);
// for the SrsHls to callback to request the sequence headers.
virtual int on_hls_start();
public:
virtual bool can_publish();
virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata);
virtual int on_audio(SrsCommonMessage* audio);
virtual int on_video(SrsCommonMessage* video);
virtual int on_publish(SrsRequest* req);
/**
* publish stream event notify.
* @param _req the request from client, the source will deep copy it,
* for when reload the request of client maybe invalid.
*/
virtual int on_publish(SrsRequest* _req);
virtual void on_unpublish();
public:
virtual int create_consumer(SrsConsumer*& consumer);
virtual void on_consumer_destroy(SrsConsumer* consumer);
virtual void set_cache(bool enabled);
private:
virtual int create_forwarders();
virtual void destroy_forwarders();
};
#endif
... ...