mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
merged master
This commit is contained in:
commit
c87e55a2cc
45 changed files with 2155 additions and 1144 deletions
71
README.md
71
README.md
|
@ -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>
|
||||
step 3(optinal): start srs listen at 19350 to forward to<br/>
|
||||
<strong>step 2:</strong> start srs <br/>
|
||||
<pre>
|
||||
./objs/simple_rtmp_server -c conf/srs.19350.conf
|
||||
./objs/srs -c conf/srs.conf
|
||||
</pre>
|
||||
step 4(optional): start nginx for HLS <br/>
|
||||
<strong>step 3(optinal):</strong> start srs listen at 19350 to forward to<br/>
|
||||
<pre>
|
||||
./objs/srs -c conf/srs.19350.conf
|
||||
</pre>
|
||||
<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} &&
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
# 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 {
|
||||
|
|
24
trunk/configure
vendored
24
trunk/configure
vendored
|
@ -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"
|
||||
|
|
1
trunk/research/players/index.html
Normal file → Executable file
1
trunk/research/players/index.html
Normal file → Executable file
|
@ -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>
|
||||
|
|
2
trunk/research/players/jwplayer5/index.html
Normal file → Executable file
2
trunk/research/players/jwplayer5/index.html
Normal file → Executable file
|
@ -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>
|
||||
|
|
2
trunk/research/players/jwplayer6/index.html
Normal file → Executable file
2
trunk/research/players/jwplayer6/index.html
Normal file → Executable file
|
@ -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>
|
||||
|
|
2
trunk/research/players/osmf/index.html
Normal file → Executable file
2
trunk/research/players/osmf/index.html
Normal file → Executable file
|
@ -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>
|
||||
|
|
BIN
trunk/research/players/rtmp/RtmpPlayer.swf
Normal file → Executable file
BIN
trunk/research/players/rtmp/RtmpPlayer.swf
Normal file → Executable file
Binary file not shown.
2
trunk/research/players/rtmp/index.html
Normal file → Executable file
2
trunk/research/players/rtmp/index.html
Normal file → Executable file
|
@ -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();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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,13 +483,103 @@ 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();
|
||||
|
||||
srs_freep(pthread);
|
||||
}
|
||||
|
||||
int SrsEncoder::on_publish(SrsRequest* req)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
ret = parse_scope_engines(req);
|
||||
|
||||
// ignore the loop encoder
|
||||
if (ret == ERROR_ENCODER_LOOP) {
|
||||
clear_engines();
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// return for error or no engine.
|
||||
if (ret != ERROR_SUCCESS || ffmpegs.empty()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// start thread to run all encoding engines.
|
||||
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
||||
srs_error("st_thread_create failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SrsEncoder::on_unpublish()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
SrsFFMPEG* SrsEncoder::at(int index)
|
||||
{
|
||||
return ffmpegs[index];
|
||||
}
|
||||
|
||||
int SrsEncoder::parse_scope_engines(SrsRequest* req)
|
||||
|
@ -531,60 +621,6 @@ int SrsEncoder::parse_scope_engines(SrsRequest* req)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsEncoder::on_publish(SrsRequest* req)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
ret = parse_scope_engines(req);
|
||||
|
||||
// ignore the loop encoder
|
||||
if (ret == ERROR_ENCODER_LOOP) {
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// return for error or no engine.
|
||||
if (ret != ERROR_SUCCESS || ffmpegs.empty()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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;
|
||||
srs_error("st_thread_create failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SrsEncoder::on_unpublish()
|
||||
{
|
||||
if (tid) {
|
||||
loop = false;
|
||||
st_thread_interrupt(tid);
|
||||
st_thread_join(tid, NULL);
|
||||
tid = NULL;
|
||||
}
|
||||
|
||||
clear_engines();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
SrsFFMPEG* SrsEncoder::at(int index)
|
||||
{
|
||||
return ffmpegs[index];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ret = client->send_message(msg);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
srs_assert(msg);
|
||||
msgs[i] = NULL;
|
||||
|
||||
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);
|
||||
private:
|
||||
virtual int open_socket();
|
||||
virtual int connect_server();
|
||||
private:
|
||||
// interface ISrsThreadHandler.
|
||||
public:
|
||||
virtual int cycle();
|
||||
private:
|
||||
virtual void close_underlayer_socket();
|
||||
virtual int connect_server();
|
||||
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*)×tamp;
|
||||
*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*)×tamp;
|
||||
*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*)×tamp;
|
||||
*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*)×tamp;
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
SrsReloadHandler::~SrsReloadHandler()
|
||||
ISrsReloadHandler::~ISrsReloadHandler()
|
||||
{
|
||||
}
|
||||
|
||||
int SrsReloadHandler::on_reload_listen()
|
||||
int ISrsReloadHandler::on_reload_listen()
|
||||
{
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int SrsReloadHandler::on_reload_pithy_print()
|
||||
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 ISrsReloadHandler::on_reload_hls(string /*vhost*/)
|
||||
{
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
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
|
|
@ -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()
|
||||
{
|
||||
srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd);
|
||||
}
|
||||
|
||||
int SrsListener::cycle()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
log_context->generate_id();
|
||||
srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd);
|
||||
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));
|
||||
|
||||
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);
|
||||
if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) {
|
||||
srs_warn("accept client error. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* SrsListener::listen_thread(void* arg)
|
||||
{
|
||||
SrsListener* obj = (SrsListener*)arg;
|
||||
srs_assert(obj != NULL);
|
||||
|
||||
obj->loop = true;
|
||||
obj->listen_cycle();
|
||||
srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
|
||||
|
||||
return NULL;
|
||||
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,19 +111,152 @@ int SrsRtmpJitter::get_time()
|
|||
return (int)last_pkt_correct_time;
|
||||
}
|
||||
|
||||
SrsMessageQueue::SrsMessageQueue()
|
||||
{
|
||||
queue_size_ms = 0;
|
||||
av_start_time = av_end_time = -1;
|
||||
}
|
||||
|
||||
SrsMessageQueue::~SrsMessageQueue()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void SrsMessageQueue::set_queue_size(double queue_size)
|
||||
{
|
||||
queue_size_ms = (int)(queue_size * 1000);
|
||||
}
|
||||
|
||||
int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
msgs.push_back(msg);
|
||||
|
||||
while (av_end_time - av_start_time > queue_size_ms) {
|
||||
shrink();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsMessageQueue::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if (msgs.empty()) {
|
||||
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];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
pmsgs[i] = msgs[i];
|
||||
}
|
||||
|
||||
SrsSharedPtrMessage* last = msgs[count - 1];
|
||||
av_start_time = last->header.timestamp;
|
||||
|
||||
if (count == (int)msgs.size()) {
|
||||
msgs.clear();
|
||||
} else {
|
||||
msgs.erase(msgs.begin(), msgs.begin() + count);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SrsMessageQueue::shrink()
|
||||
{
|
||||
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()) {
|
||||
if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no iframe, clear the queue.
|
||||
if (iframe_index < 0) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
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 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()
|
||||
{
|
||||
clear();
|
||||
|
||||
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()
|
||||
|
@ -138,46 +273,21 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int tba, int tbv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// TODO: check the queue size and drop packets if overflow.
|
||||
msgs.push_back(msg);
|
||||
if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count)
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if (msgs.empty()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// paused, return nothing.
|
||||
if (paused) {
|
||||
if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) {
|
||||
shrink();
|
||||
}
|
||||
return ret;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
if (max_count == 0) {
|
||||
count = (int)msgs.size();
|
||||
} else {
|
||||
count = srs_min(max_count, (int)msgs.size());
|
||||
}
|
||||
|
||||
pmsgs = new SrsSharedPtrMessage*[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
pmsgs[i] = msgs[i];
|
||||
}
|
||||
|
||||
if (count == (int)msgs.size()) {
|
||||
msgs.clear();
|
||||
} else {
|
||||
msgs.erase(msgs.begin(), msgs.begin() + count);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return queue->get_packets(max_count, pmsgs, count);
|
||||
}
|
||||
|
||||
int SrsConsumer::on_play_client_pause(bool is_pause)
|
||||
|
@ -190,68 +300,6 @@ int SrsConsumer::on_play_client_pause(bool 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;
|
||||
if (msg->header.is_video()) {
|
||||
has_video = true;
|
||||
if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) {
|
||||
iframe = it;
|
||||
frame_to_remove = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void SrsConsumer::clear()
|
||||
{
|
||||
std::vector<SrsSharedPtrMessage*>::iterator it;
|
||||
for (it = msgs.begin(); it != msgs.end(); ++it) {
|
||||
SrsSharedPtrMessage* msg = *it;
|
||||
srs_freep(msg);
|
||||
}
|
||||
msgs.clear();
|
||||
}
|
||||
|
||||
SrsGopCache::SrsGopCache()
|
||||
{
|
||||
cached_video_count = 0;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
100
trunk/src/core/srs_core_source.hpp
Normal file → Executable file
100
trunk/src/core/srs_core_source.hpp
Normal file → Executable file
|
@ -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,24 +56,64 @@ 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.
|
||||
*/
|
||||
virtual int get_time();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -80,11 +122,13 @@ 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
|
||||
|
|
156
trunk/src/core/srs_core_thread.cpp
Normal file
156
trunk/src/core/srs_core_thread.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 winlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <srs_core_thread.hpp>
|
||||
|
||||
#include <srs_core_error.hpp>
|
||||
#include <srs_core_log.hpp>
|
||||
|
||||
ISrsThreadHandler::ISrsThreadHandler()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsThreadHandler::~ISrsThreadHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void ISrsThreadHandler::on_enter_loop()
|
||||
{
|
||||
}
|
||||
|
||||
int ISrsThreadHandler::on_before_cycle()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ISrsThreadHandler::on_end_cycle()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ISrsThreadHandler::on_leave_loop()
|
||||
{
|
||||
}
|
||||
|
||||
SrsThread::SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms)
|
||||
{
|
||||
handler = thread_handler;
|
||||
cycle_interval_milliseconds = interval_ms;
|
||||
|
||||
tid = NULL;
|
||||
loop = false;
|
||||
}
|
||||
|
||||
SrsThread::~SrsThread()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
int SrsThread::start()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
if(tid) {
|
||||
srs_info("thread already running.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if((tid = st_thread_create(thread_fun, this, 1, 0)) == NULL){
|
||||
ret = ERROR_ST_CREATE_CYCLE_THREAD;
|
||||
srs_error("st_thread_create failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SrsThread::stop()
|
||||
{
|
||||
if (tid) {
|
||||
loop = false;
|
||||
|
||||
// the interrupt will cause the socket to read/write error,
|
||||
// which will terminate the cycle thread.
|
||||
st_thread_interrupt(tid);
|
||||
|
||||
// wait the thread to exit.
|
||||
st_thread_join(tid, NULL);
|
||||
|
||||
tid = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsThread::thread_cycle()
|
||||
{
|
||||
int ret = ERROR_SUCCESS;
|
||||
|
||||
srs_assert(handler);
|
||||
|
||||
log_context->generate_id();
|
||||
srs_trace("thread cycle start");
|
||||
|
||||
handler->on_end_cycle();
|
||||
|
||||
loop = true;
|
||||
while (loop) {
|
||||
if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
|
||||
srs_warn("thread on before cycle failed, ignored and retry, ret=%d", ret);
|
||||
goto failed;
|
||||
}
|
||||
srs_info("thread on before cycle success");
|
||||
|
||||
if ((ret = handler->cycle()) != ERROR_SUCCESS) {
|
||||
srs_warn("thread cycle failed, ignored and retry, ret=%d", ret);
|
||||
goto failed;
|
||||
}
|
||||
srs_info("thread cycle success");
|
||||
|
||||
if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
|
||||
srs_warn("thread on end cycle failed, ignored and retry, ret=%d", ret);
|
||||
goto failed;
|
||||
}
|
||||
srs_info("thread on end cycle success");
|
||||
|
||||
failed:
|
||||
if (!loop) {
|
||||
break;
|
||||
}
|
||||
|
||||
st_usleep(cycle_interval_milliseconds * 1000);
|
||||
}
|
||||
|
||||
handler->on_leave_loop();
|
||||
srs_trace("thread cycle finished");
|
||||
}
|
||||
|
||||
void* SrsThread::thread_fun(void* arg)
|
||||
{
|
||||
SrsThread* obj = (SrsThread*)arg;
|
||||
srs_assert(obj);
|
||||
|
||||
obj->thread_cycle();
|
||||
|
||||
return NULL;
|
||||
}
|
98
trunk/src/core/srs_core_thread.hpp
Normal file
98
trunk/src/core/srs_core_thread.hpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 winlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SRS_CORE_THREAD_HPP
|
||||
#define SRS_CORE_THREAD_HPP
|
||||
|
||||
/*
|
||||
#include <srs_core_thread.hpp>
|
||||
*/
|
||||
#include <srs_core.hpp>
|
||||
|
||||
/**
|
||||
* the handler for the thread, callback interface.
|
||||
* the thread model defines as:
|
||||
* handler->on_enter_loop()
|
||||
* while loop:
|
||||
* handler->on_before_cycle()
|
||||
* handler->cycle()
|
||||
* handler->on_end_cycle()
|
||||
* if !loop then break for user stop thread.
|
||||
* sleep(CycleIntervalMilliseconds)
|
||||
* handler->on_leave_loop()
|
||||
* when stop, the thread will interrupt the st_thread,
|
||||
* which will cause the socket to return error and
|
||||
* terminate the cycle thread.
|
||||
*/
|
||||
class ISrsThreadHandler
|
||||
{
|
||||
public:
|
||||
ISrsThreadHandler();
|
||||
virtual ~ISrsThreadHandler();
|
||||
public:
|
||||
virtual void on_enter_loop();
|
||||
virtual int on_before_cycle();
|
||||
virtual int cycle() = 0;
|
||||
virtual int on_end_cycle();
|
||||
virtual void on_leave_loop();
|
||||
};
|
||||
|
||||
/**
|
||||
* provides servies from st_thread_t,
|
||||
* for common thread usage.
|
||||
*/
|
||||
class SrsThread
|
||||
{
|
||||
private:
|
||||
st_thread_t tid;
|
||||
bool loop;
|
||||
private:
|
||||
ISrsThreadHandler* handler;
|
||||
int64_t cycle_interval_milliseconds;
|
||||
public:
|
||||
/**
|
||||
* initialize the thread.
|
||||
* @param thread_handler, the cycle handler for the thread.
|
||||
* @param interval_ms, the sleep interval when cycle finished.
|
||||
*/
|
||||
SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms);
|
||||
virtual ~SrsThread();
|
||||
public:
|
||||
/**
|
||||
* start the thread, invoke the cycle of handler util
|
||||
* user stop the thread.
|
||||
* @remark ignore any error of cycle of handler.
|
||||
* @remark user can start multiple times, ignore if already started.
|
||||
*/
|
||||
virtual int start();
|
||||
/**
|
||||
* stop the thread, wait for the thread to terminate.
|
||||
* @remark user can stop multiple times, ignore if already stopped.
|
||||
*/
|
||||
virtual void stop();
|
||||
private:
|
||||
virtual void thread_cycle();
|
||||
static void* thread_fun(void* arg);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,60 +1,62 @@
|
|||
file
|
||||
main readonly separator,
|
||||
..\main\srs_main_server.cpp,
|
||||
auto readonly separator,
|
||||
..\..\objs\srs_auto_headers.hpp,
|
||||
core readonly separator,
|
||||
..\core\srs_core.hpp,
|
||||
..\core\srs_core.cpp,
|
||||
..\core\srs_core_error.hpp,
|
||||
..\core\srs_core_error.cpp,
|
||||
..\core\srs_core_autofree.hpp,
|
||||
..\core\srs_core_autofree.cpp,
|
||||
..\core\srs_core_server.hpp,
|
||||
..\core\srs_core_server.cpp,
|
||||
..\core\srs_core_reload.hpp,
|
||||
..\core\srs_core_reload.cpp,
|
||||
..\core\srs_core_config.hpp,
|
||||
..\core\srs_core_config.cpp,
|
||||
..\core\srs_core_refer.hpp,
|
||||
..\core\srs_core_refer.cpp,
|
||||
..\core\srs_core_conn.hpp,
|
||||
..\core\srs_core_conn.cpp,
|
||||
..\core\srs_core_client.hpp,
|
||||
..\core\srs_core_client.cpp,
|
||||
..\core\srs_core_http.hpp,
|
||||
..\core\srs_core_http.cpp,
|
||||
..\core\srs_core_source.hpp,
|
||||
..\core\srs_core_source.cpp,
|
||||
..\core\srs_core_forward.hpp,
|
||||
..\core\srs_core_forward.cpp,
|
||||
..\core\srs_core_encoder.hpp,
|
||||
..\core\srs_core_encoder.cpp,
|
||||
..\core\srs_core_hls.hpp,
|
||||
..\core\srs_core_hls.cpp,
|
||||
..\core\srs_core_codec.hpp,
|
||||
..\core\srs_core_codec.cpp,
|
||||
..\core\srs_core_rtmp.hpp,
|
||||
..\core\srs_core_rtmp.cpp,
|
||||
..\core\srs_core_handshake.hpp,
|
||||
..\core\srs_core_handshake.cpp,
|
||||
..\core\srs_core_protocol.hpp,
|
||||
..\core\srs_core_protocol.cpp,
|
||||
..\core\srs_core_amf0.hpp,
|
||||
..\core\srs_core_amf0.cpp,
|
||||
..\core\srs_core_stream.hpp,
|
||||
..\core\srs_core_stream.cpp,
|
||||
..\core\srs_core_socket.hpp,
|
||||
..\core\srs_core_socket.cpp,
|
||||
..\core\srs_core_buffer.hpp,
|
||||
..\core\srs_core_buffer.cpp,
|
||||
..\core\srs_core_pithy_print.hpp,
|
||||
..\core\srs_core_pithy_print.cpp,
|
||||
..\core\srs_core_log.hpp,
|
||||
..\core\srs_core_log.cpp,
|
||||
research readonly separator,
|
||||
..\..\research\ts_info.cc;
|
||||
main readonly separator,
|
||||
..\main\srs_main_server.cpp,
|
||||
auto readonly separator,
|
||||
..\..\objs\srs_auto_headers.hpp,
|
||||
core readonly separator,
|
||||
..\core\srs_core.hpp,
|
||||
..\core\srs_core.cpp,
|
||||
..\core\srs_core_amf0.hpp,
|
||||
..\core\srs_core_amf0.cpp,
|
||||
..\core\srs_core_autofree.hpp,
|
||||
..\core\srs_core_autofree.cpp,
|
||||
..\core\srs_core_buffer.hpp,
|
||||
..\core\srs_core_buffer.cpp,
|
||||
..\core\srs_core_client.hpp,
|
||||
..\core\srs_core_client.cpp,
|
||||
..\core\srs_core_codec.hpp,
|
||||
..\core\srs_core_codec.cpp,
|
||||
..\core\srs_core_config.hpp,
|
||||
..\core\srs_core_config.cpp,
|
||||
..\core\srs_core_conn.hpp,
|
||||
..\core\srs_core_conn.cpp,
|
||||
..\core\srs_core_encoder.hpp,
|
||||
..\core\srs_core_encoder.cpp,
|
||||
..\core\srs_core_error.hpp,
|
||||
..\core\srs_core_error.cpp,
|
||||
..\core\srs_core_forward.hpp,
|
||||
..\core\srs_core_forward.cpp,
|
||||
..\core\srs_core_handshake.hpp,
|
||||
..\core\srs_core_handshake.cpp,
|
||||
..\core\srs_core_hls.hpp,
|
||||
..\core\srs_core_hls.cpp,
|
||||
..\core\srs_core_http.hpp,
|
||||
..\core\srs_core_http.cpp,
|
||||
..\core\srs_core_log.hpp,
|
||||
..\core\srs_core_log.cpp,
|
||||
..\core\srs_core_pithy_print.hpp,
|
||||
..\core\srs_core_pithy_print.cpp,
|
||||
..\core\srs_core_protocol.hpp,
|
||||
..\core\srs_core_protocol.cpp,
|
||||
..\core\srs_core_refer.hpp,
|
||||
..\core\srs_core_refer.cpp,
|
||||
..\core\srs_core_reload.hpp,
|
||||
..\core\srs_core_reload.cpp,
|
||||
..\core\srs_core_rtmp.hpp,
|
||||
..\core\srs_core_rtmp.cpp,
|
||||
..\core\srs_core_thread.hpp,
|
||||
..\core\srs_core_thread.cpp,
|
||||
..\core\srs_core_server.hpp,
|
||||
..\core\srs_core_server.cpp,
|
||||
..\core\srs_core_stream.hpp,
|
||||
..\core\srs_core_stream.cpp,
|
||||
..\core\srs_core_socket.hpp,
|
||||
..\core\srs_core_socket.cpp,
|
||||
..\core\srs_core_source.hpp,
|
||||
..\core\srs_core_source.cpp,
|
||||
research readonly separator,
|
||||
..\..\research\ts_info.cc;
|
||||
|
||||
mainconfig
|
||||
"" = "MAIN";
|
||||
"" = "MAIN";
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue