diff --git a/.gitignore b/.gitignore index 620d3dc8..d2533b10 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,16 @@ *.lai *.la *.a + +# by winlin +*.pyc +*.swp +/trunk/Makefile +/trunk/objs +/trunk/research/librtmp/objs +/trunk/3rdparty/ccache/ccache-3.1.9 +/trunk/3rdparty/gprof/graphviz-2.36.0 +/trunk/research/api-server/static-dir/crossdomain.xml +/trunk/research/api-server/static-dir/forward +/trunk/research/api-server/static-dir/live +/trunk/research/api-server/static-dir/players diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 00000000..1d3b6818 --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,24 @@ +There are three types of people that have contributed to the SRS project: +1. PRIMARY: Contribute important features and >10% code. Names of all PRIMARY response in NetConnection.connect and metadata. +2. AUTHORS: Contribute features and 1%~10% code. Names of all PRIMARY response in NetConnection.connect and metadata. +3. CONTRIBUTORS: Submit patches, report bugs, add translations, help answer newbie questions, and generally make SRS that much better. + +PRIMARY ordered by first contribution. +* winlin "Plan, arch, implement SRS1.0 and SRS2.0" + +AUTHORS ordered by first contribution. +* wenjie.zhao<740936897@qq.com> "The bandwidth test module" + +CONTRIBUTORS ordered by first contribution. +* xiangcheng.liu "Bug fixed" +* naijia.liu "Performance benchmark" +* alcoholyi "Bug fixed" +* byteman "Bug fixed" +* chad.wang "Bug fixed" +* suhetao "Bug fixed" +* Johnny "Create domain ossrs.net" +* karthikeyan "Bug fixed" +* StevenLiu "Build SRS on Darwin OSX" +* zhengfl "Bug fixed" +* tufang14 "Bug fixed" + diff --git a/DONATIONS.txt b/DONATIONS.txt new file mode 100644 index 00000000..271c0f9f --- /dev/null +++ b/DONATIONS.txt @@ -0,0 +1,30 @@ +Donations ordered by first donation. + +=========================================================== +2014 + +RMB 1000+ +* [2014-04-25 13:21] 刘连响 +* [2014-04-25 13:31] 郭强 +* [2014-07-30 11:29] 周凯 +* [2014-08-15 10:55] 雷健 + +RMB 500-999 +* [2014-07-24 08:52] 黄英才 + +RMB 100-499 +* [2014-04-25 13:25] 张瑞圣 +* [2014-05-12 10:22] 陈晨 +* [2014-08-07 22:56] 陈亮 +* [2014-08-19 20:00] Matthew +* [2014-09-05 16:13] 于冰 +* [2014-11-19 22:38] 夏江龙 + +RMB 50-99 +* [2014-06-17 17:57] 陈江兵 +* [2014-08-04 10:47] 宋志 +* [2014-08-15 13:31] ZACH + +RMB 0.01-49 +* [2014-08-20 20:13] 林瑞潮 + diff --git a/LICENSE b/LICENSE index f37c3072..c09eb457 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 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 @@ -18,3 +18,4 @@ 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. + diff --git a/README.md b/README.md index 83ba3905..dc0fdd50 100755 --- a/README.md +++ b/README.md @@ -1,239 +1,575 @@ -simple-rtmp-server -================== +#Simple-RTMP-Server -srs(simple rtmp origin live server) over state-threads.
-srs is a simple, high-performance, running in single process, origin live server.
-srs supports rtmp, HLS, transcoding, forward, http hooks.
-blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
-see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
-see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server) +SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。 -### Contributors -winlin(winterserver): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) +下载发布版(github): +[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.21.zip) +其他[more...](http://winlinvip.github.io/srs.release/releases/)
+下载发布版(国内阿里云镜像): +[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.21.zip) +其他[more...](http://www.ossrs.net/srs.release/releases/)
+QQ群: 365936885, by wenjie
+同类产品:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), [RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) + +获得源码(github): [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://github.com/winlinvip/simple-rtmp-server.git +``` + +获得源码(国内CSDN镜像): [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://code.csdn.net/winlinvip/srs-csdn.git +``` + +获得源码(国内OSChina镜像): [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://git.oschina.net/winlinvip/srs.oschina.git +``` + +报告问题(BugReport): [https://github.com/winlinvip/simple-rtmp-server/issues/new](https://github.com/winlinvip/simple-rtmp-server/issues/new)
+中文资料(Wiki): [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
+使用步骤(Usage): [https://github.com/winlinvip/simple-rtmp-server#usage](#usage)
+公用机器(LiveShow): [https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LiveShow](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LiveShow)
+捐款(Donation): [GitHub](http://winlinvip.github.io/srs.release/donation/index.html) +或 [阿里云镜像](http://www.ossrs.net/srs.release/donation/index.html) ,查看 +[捐献墙(Donations)](https://github.com/winlinvip/simple-rtmp-server/blob/develop/DONATIONS.txt)
+ +## About + +SRS(SIMPLE RTMP Server) over state-threads created in 2013.10. + +SRS delivers rtmp/hls live on x86/x64/arm/mips linux, +supports origin/edge/vhost and transcode/ingest and dvr/forward +and http-api/http-callback/reload, introduces tracable +session-oriented log, exports client srs-librtmp, +provides EN/CN wiki and the most simple architecture. + +SRS focus on small problem domain, which is the most complex for all software(see OOAD). +Because of lack of deveoper resource, SRS only provides features which is the most popular +for internet. SRS is simple for and only for problem domain is simplified. + +SRS is a simple, RTMP( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryRTMP) +), +HLS( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS) +), +high-performance(2.7k+ clients)( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance) +), +single processes, edge/origin live server, +x86/x64/arm( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLinuxArm) +), +compile depends on st( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Architecture), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Architecture) +)(required), +[ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser), +use [nginx](http://nginx.org/), [ffmpeg](http://ffmpeg.org/) and +[cherrypy](http://www.cherrypy.org/) as external tools. that is, only need st to run srs for +minimum run. see Build( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Build) +). + +SRS supports vhost( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_RtmpUrlVhost) +), +rtmp(encoder push( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryRTMP) +), +client/edge( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Edge), +) pull), +ingester(srs pull)( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Ingest) +), +HLS( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS) +), +HLS audio only( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS#hlsaudioonly) +), +transcoding( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FFMPEG) +), +forward( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FFMPEG) +), +http hooks( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_HTTPCallback) +), +http api( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_HTTPApi) +), +http server( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_HTTPServer) +), +dvr( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DVR) +) and +SRS-librtmp( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLibrtmp) +). + +SRS-librtmp( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLibrtmp) +) +is a client library, only depends on c++ and socket, with +examples( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp#srs-librtmp-examples), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLibrtmp#srs-librtmp-examples) +)(to play, +publish, ingest flv/rtmp, inject flv, +SRS-librtmp( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLibrtmp) +) +provides apis to support RTMP, FLV and AMF0. + +WebSite: [http://ossrs.net](http://ossrs.net)
+Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
+Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
+CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn)
+See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
+Github DEMO: [demo with your SRS](http://winlinvip.github.io/srs.release/trunk/research/players/srs_player.html?server=192.168.1.170&vhost=192.168.1.170)
+Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
+StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), +[NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), +[RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), +[FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) + +## AUTHORS + +There are three types of people that have contributed to the SRS project: +* PRIMARY: Contribute important features and >10% code. Names of all +PRIMARY response in NetConnection.connect and metadata. +* AUTHORS: Contribute features and 1%~10% code. Names of all +PRIMARY response in NetConnection.connect and metadata. +* CONTRIBUTORS: Submit patches, report bugs, add translations, help answer +newbie questions, and generally make SRS that much better. + +About all PRIMARY, AUTHORS and CONTRIBUTORS, read +[AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt). + +A big THANK YOU goes to: +* [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#bigthanks). +* Genes amd Mabbott for creating [st](https://github.com/winlinvip/state-threads)([state-threads](http://sourceforge.net/projects/state-threads/)). +* Michael Talyanksy for introducing us to use st. +* Roman Arutyunyan for creating [nginx-rtmp](https://github.com/arut/nginx-rtmp-module) for SRS to refer to. +* Joyent for creating [http-parser](https://github.com/joyent/http-parser) for http-api for SRS. +* Igor Sysoev for creating [nginx](http://nginx.org/) for SRS to refer to. +* [FFMPEG](http://ffmpeg.org/) and [libx264](http://www.videolan.org/) group for SRS to use to transcode. +* Guido van Rossum for creating Python for api-server for SRS. + +## Mirrors + +Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server), +the GIT usage( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) + +```bash +git clone https://github.com/winlinvip/simple-rtmp-server.git +``` + +CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) , +the GIT usage( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) + +```bash +git clone https://code.csdn.net/winlinvip/srs-csdn.git +``` + +OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) , +the GIT usage( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) + +```bash +git clone https://git.oschina.net/winlinvip/srs.oschina.git +``` + +Gitlab: [https://gitlab.com/winlinvip/srs-gitlab](https://gitlab.com/winlinvip/srs-gitlab) , +the GIT usage( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) + +```bash +git clone https://gitlab.com/winlinvip/srs-gitlab.git +``` + +## Usage + +Step 1: get SRS -### Usage -step 1: build srs
-
-tar xf simple-rtmp-server-*.*.tar.gz
-cd simple-rtmp-server-*.*/trunk
-./configure --with-ssl --with-hls --with-ffmpeg --with-http
-make
-
-step 2: start srs
-
-./objs/simple_rtmp_server -c conf/srs.conf
-
-step 3(optinal): start srs listen at 19350 to forward to
-
-./objs/simple_rtmp_server -c conf/srs.19350.conf
-
-step 4(optional): start nginx for HLS
-
-sudo ./objs/nginx/sbin/nginx
-
-step 5(optional): start http hooks for srs callback
-
-python ./research/api-server/server.py 8085
-
-step 6: publish live stream
-
-FMS URL: rtmp://127.0.0.1:1935/live
-Stream:  livestream
-For example, use ffmpeg to publish:
-    for((;;)); do \
-        ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-        -vcodec copy -acodec copy \
-        -f flv -y rtmp://127.0.0.1:1935/live/livestream; \
-        sleep 1; \
-    done
-
-step 7: add server ip to client hosts as demo.
-
-# edit the folowing file:
-# linux: /etc/hosts
-# windows: C:\Windows\System32\drivers\etc\hosts
-# where server ip is 192.168.2.111
-192.168.2.111 demo 
-
-step 8: play live stream.
-
-rtmp url: rtmp://demo:1935/live/livestream
-m3u8 url: http://demo:80/live/livestream.m3u8
-for android: http://demo:80/live/livestream.html
-
-step 9: play live stream auto transcoded
-
-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
-
-step 10: play live stream auto forwarded, the hls dir change to /forward
-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
+git clone https://github.com/winlinvip/simple-rtmp-server &&
+cd simple-rtmp-server/trunk
 
-### Architecture -System Architecture: +Step 2: build SRS, +Requires Centos6.x/Ubuntu12 32/64bits, others see Build( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Build) +). +
-+------------------------------------------------------+
-|             SRS(Simple Rtmp Server)                  |
-+---------------+---------------+-----------+----------+
-|   API/hook    |   Transcoder  |    HLS    |   RTMP   |
-|  http-parser  |  FFMPEG/x264  |  NGINX/ts | protocol |
-+---------------+---------------+-----------+----------+
-|              Network(state-threads)                  |
-+------------------------------------------------------+
-|      All Linux(RHEL,Centos,Ubuntu,Fedora...)         |
-+------------------------------------------------------+
+./configure && make
 
-Stream Architecture: + +Step 3: start SRS +
-        +---------+              +----------+ 
-        + Publish +              +  Deliver | 
-        +---|-----+              +----|-----+ 
-+-----------+-------------------------+----------------+
-| Encoder   | SRS(Simple Rtmp Server) |     Client     |
-+-----------+-------------------------+----------------+
-| (FMLE,    |   +-> RTMP protocol ----+-> Flash Player |
-| FFMPEG, --+-> +-> HLS/NGINX --------+-> m3u8 player  |
-| Flash,    |   +-> Fowarder ---------+-> RTMP Server  |
-| XSPLIT,   |   +-> Transcoder -------+-> RTMP Server  |
-|  ...)     |   +-> DVR --------------+-> FILE         |
-+-----------+-------------------------+----------------+
+./objs/srs -c conf/srs.conf
 
-### System Requirements +See also: +* Usage: How to delivery RTMP?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleRTMP) +) +* Usage: How to delivery HLS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleHLS) +) +* Usage: How to delivery HLS for other codec?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleTranscode2HLS) +) +* Usage: How to transode RTMP stream by SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleFFMPEG) +) +* Usage: How to forward stream to other server?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleForward) +) +* Usage: How to deploy low lantency application?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleRealtime) +) +* Usage: How to deploy SRS on ARM?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleARM), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleARM) +) +* Usage: How to ingest file/stream/device to SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleIngest) +) +* Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleHTTP) +) +* Usage: How to show the demo of SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleDemo) +) +* Usage: Solution using SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Sample), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Sample) +) +* Usage: Why SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Product) +) + +## Wiki + +SRS 1.0 wiki + +Please select your language: +* [SRS 1.0 English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home) +* [SRS 1.0 Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home) + +## Donation + +Donation:
+[http://winlinvip.github.io/srs.release/donation/index.html](http://winlinvip.github.io/srs.release/donation/index.html) OR
+[http://www.ossrs.net/srs.release/donation/index.html](http://www.ossrs.net/srs.release/donation/index.html) + +Donations:
+[https://github.com/winlinvip/simple-rtmp-server/blob/develop/DONATIONS.txt] +(https://github.com/winlinvip/simple-rtmp-server/blob/develop/DONATIONS.txt) + +## System Requirements Supported operating systems and hardware: * All Linux , both 32 and 64 bits -* All handware. - -### Summary -1. simple: also stable enough.
-2. high-performance: single-thread, async socket, event/st-thread driven.
-3. no edge server, origin server only.
-4. no vod streaming, live streaming only.
-5. no multiple processes, single process only.
-6. support vhost, support \_\_defaultVhost\_\_.
-7. support adobe rtmp live streaming.
-8. support apple hls(m3u8) live streaming.
-9. support reload config to enable changes.
-10. support cache last gop for flash player to fast startup.
-11. support listen at multiple ports.
-12. support long time(>4.6hours) publish/play.
-13. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
-14. support forward publish stream to build active-standby cluster.
-15. support broadcast by forward the stream to other servers(origin/edge).
-16. support live stream transcoding by ffmpeg.
-17. support live stream forward(acopy/vcopy) by ffmpeg.
-18. support ffmpeg filters(logo/overlay/crop), x264 params.
-19. support audio transcode only, speex/mp3 to aac
-20. support http callback api hooks(for authentication and injection).
-21. [plan] support network based cli and json result.
-22. [plan] support bandwidth test api and flash client.
-23. [plan] support adobe flash refer/token/swf verification.
-24. [plan] support adobe amf3 codec.
-25. [plan] support dvr(record live to vod file)
-26. [plan] support FMS edge protocol
-27. [plan] support encryption: RTMPE/RTMPS, HLS DRM
-28. [plan] support RTMPT, http to tranverse firewalls
-29. [plan] support file source, transcoding file to live stream
- -### Performance -1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. -2. 600 connections, 300Mbps, 500kbps, CPU 32.1%, 9808KB. -3. 900 connections, 450Mbps, 500kbps, CPU 49.9%, 11MB. -4. 1200 connections, 600Mbps, 500kbps, CPU 72.4%, 15MB. -5. 1500 connections, 750Mbps, 500kbps, CPU 81.9%, 28MB. -6. 1800 connections, 900Mbps, 500kbps, CPU 90.2%, 41MB. -
-----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
-usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
- 58   9  32   0   0   1|   0  4168k| 277M  277M|   0     0 |  29k   25k
- 61   8  30   0   0   1|   0  1168k| 336M  336M|   0     0 |  29k   24k
- 63   8  27   0   0   1|   0  2240k| 124M  124M|   0     0 |  32k   33k
- 62   8  28   0   0   1|   0  1632k| 110M  110M|   0     0 |  31k   33k
- 67   9  23   0   0   2|   0  1604k| 130M  130M|   0     0 |  33k   32k
- 63   9  27   0   0   2|   0  1496k| 145M  145M|   0     0 |  32k   32k
- 61   9  29   0   0   1|   0  1112k| 132M  132M|   0     0 |  32k   33k
- 63   9  27   0   0   2|   0  1220k| 145M  145M|   0     0 |  32k   33k
- 53   7  40   0   0   1|   0  1360k| 115M  115M|   0     0 |  24k   26k
- 51   7  41   0   0   1|   0  1184k| 146M  146M|   0     0 |  24k   27k
- 39   6  54   0   0   1|   0  1284k| 105M  105M|   0     0 |  22k   28k
- 41   6  52   0   0   1|   0  1264k| 116M  116M|   0     0 |  25k   28k
- 48   6  45   0   0   1|   0  1272k| 143M  143M|   0     0 |  27k   27k
-
+* All hardware with x86/x86_64/arm/mips cpu. + +## Summary +1. 简洁稳定:Simple, also stable enough. +1. 高性能:[High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance): single-thread, async socket, event/st-thread driven. +1. 高并发:[High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB +1. RTMP源站:Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP). +1. CDN边缘(上下行加速):Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) for CDN, push/pull stream from any RTMP server +1. 单进程(无多进程):Support single process; no multiple processes. +1. 支持Vhost:Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), support \_\_defaultVhost\_\_. +1. 直播(无点播):Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP) live streaming; no vod streaming. +1. 苹果HLS:Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) live streaming. +1. 支持纯音频HLS:Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly) live streaming. +1. 支持Reload:Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config to enable changes. +1. 支持GopCache:Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency#gop-cache) for flash player to fast startup. +1. 侦听多端口:Support listen at multiple ports. +1. 长时间推流:Support long time(>4.6hours) publish/play. +1. 转发流:Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) in master-slave mode. +1. 流转码:Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) by ffmpeg. +1. 支持FFMPEG滤镜:Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. +1. 只转码音频:Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) only, speex/mp3 to aac +1. 支持HTTP回调:Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback)(for authentication and injection). +1. 带宽测速:Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_BandwidthTestTool) api and flash client. +1. 演示页面:Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). +1. 视频会议演示:[Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). +1. 中文Wiki:Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home), in Chineses. +1. 客户端库:Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp) +1. 支持ARM平台:Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +1. 支持Init.d脚本:Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService) and packge script, log to file. +1. 支持ATC:Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) for HLS/HDS to support backup(failover) +1. 支持HTTP-RESTful-API:Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi). +1. 采集流:Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). +1. 支持录制:Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), record live to flv file for vod. +1. 可追溯日志:Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog). +1. 支持FMS-Token穿越:Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse) for fms origin authenticate. +1. 全面的Utest:Support system full utest on gtest. +1. Stable [1.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/1.0release) and +[2.0dev branch](https://github.com/winlinvip/simple-rtmp-server/tree/master). + +## Releases +* 2015-01-15, [Release v1.0r1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0r1), bug fixed, 1.0.21, 59472 lines.
+* 2014-12-05, [Release v1.0](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0), all bug fixed, 1.0.10, 59391 lines.
+* 2014-10-09, [Release v1.0-beta](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta), all bug fixed, 1.0.0, 59316 lines.
+* 2014-08-03, [Release v1.0-mainline7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline7), config utest, all bug fixed. 57432 lines.
+* 2014-07-13, [Release v1.0-mainline6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline6), core/kernel/rtmp utest, refine bandwidth(as/js/srslibrtmp library). 50029 lines.
+* 2014-06-27, [Release v1.0-mainline5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline5), refine perf 3k+ clients, edge token traverse, [srs monitor](http://ossrs.net:1977), 30days online. 41573 lines.
+* 2014-05-28, [Release v1.0-mainline4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline4), support heartbeat, tracable log, fix mem leak and bugs. 39200 lines.
+* 2014-05-18, [Release v1.0-mainline3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline3), support mips, fms origin, json(http-api). 37594 lines.
+* 2014-04-28, [Release v1.0-mainline2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline2), support [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), android, [edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge). 35255 lines.
+* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest). 30000 lines.
+* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). 20926 lines.
+* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
+* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support [live stream transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG). 17605 lines.
+* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.
+* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.
+* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support [reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config, pause, longtime publish/play. 12500 lines.
+* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), refer, gop cache, listen multiple ports. 11773 lines.
+* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support [rtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMPHandshake) flash publish, h264, time jitter correct. 10125 lines.
+* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support [rtmp FMLE/FFMPEG publish](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), vp6. 8287 lines.
+* 2013-10-17, Created.
-### Releases -* 2013-12-08, [release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update st-load. 19186 lines.
-* 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.
-* 2013-11-29, [release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.
-* 2013-11-26, [release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.
-* 2013-11-10, [release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support reload config, pause, longtime publish/play. 12500 lines.
-* 2013-11-04, [release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support vhost, refer, gop cache, listen multiple ports. 11773 lines.
-* 2013-10-25, [release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support rtmp flash publish, h264, time jitter correct. 10125 lines.
-* 2013-10-23, [release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support rtmp FMLE/FFMPEG publish, vp6. 8287 lines.
-* 2013-10-17, created.
- -### Compare -* srs v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com).
-* srs v0.6: 16094 lines. important feature forward for CDN.
-* srs v0.5: 14449 lines. implements HLS feature refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.4: 12500 lines. important feature reload for CDN.
-* srs v0.3: 11773 lines. implements vhost feature refer to [FMS](http://www.adobe.com/products/adobe-media-server-family.html).
-* srs v0.2: 10125 lines. implements rtmp protocol stack refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.1: 8287 lines. base on state-threads.
-* nginx-rtmp v1.0.4: 26786 lines
-* nginx v1.5.0: 139524 lines
- -### History +## History +* v1.0, 2015-01-15, [1.0r1 release(1.0.21)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0r1) released. 59472 lines. +* v1.0, 2015-01-08, hotfix [#281](https://github.com/winlinvip/simple-rtmp-server/issues/281), fix hls bug ignore type-9 send aud. 1.0.20 +* v1.0, 2015-01-03, hotfix to remove the pageUrl for http callback. 1.0.19 +* v1.0, 2015-01-02, hotfix [#207](https://github.com/winlinvip/simple-rtmp-server/issues/207), trim the last 0 of log. 1.0.18 +* v1.0, 2015-01-02, hotfix [#216](https://github.com/winlinvip/simple-rtmp-server/issues/216), http-callback post in application/json content-type. 1.0.17 +* v1.0, 2015-01-01, hotfix [#270](https://github.com/winlinvip/simple-rtmp-server/issues/270), memory leak for http client post. 1.0.16 +* v1.0, 2014-12-29, hotfix [#267](https://github.com/winlinvip/simple-rtmp-server/issues/267), the forward dest ep should use server. 1.0.15 +* v1.0, 2014-12-29, hotfix [#268](https://github.com/winlinvip/simple-rtmp-server/issues/268), the hls pcr is negative when startup. 1.0.14 +* v1.0, 2014-12-26, use master as main stable branch with hotfixes. 1.0.13 +* v1.0, 2014-12-22, hotfix [#264](https://github.com/winlinvip/simple-rtmp-server/issues/264), ignore NALU when sequence header to make HLS happy. 1.0.12 +* v1.0, 2014-12-20, hotfix [#264](https://github.com/winlinvip/simple-rtmp-server/issues/264), support disconnect publish connect when hls error. 1.0.11 +* v1.0, 2014-12-05, [1.0 release(1.0.10)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0) released. 59391 lines. +* v1.0, 2014-12-02, hotfix [#239](https://github.com/winlinvip/simple-rtmp-server/pull/239), traverse the token before response connect. 1.0.10. +* v1.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS of SRS. 1.0.8. +* v1.0, 2014-11-18, all wiki translated to English. 1.0.7. +* v1.0, 2014-11-13, hotfix [#200](https://github.com/winlinvip/simple-rtmp-server/issues/200), deadloop when read/write 0 and ETIME. 1.0.6. +* v1.0, 2014-11-06, use number for macro VERSION_MAJOR, VERSION_MINOR and VERSION_REVISION. 1.0.5. +* v1.0, 2014-10-24, hotfix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), drop connect args when not object. 1.0.3. +* v1.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 1.0.2. +* v1.0, 2014-10-19, hotfix [#183](https://github.com/winlinvip/simple-rtmp-server/issues/183), donot support AnnexB when decoding RTMP body for HLS. 1.0.1. +* v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. +* v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. +* v1.0, 2014-10-08, fix [#162](https://github.com/winlinvip/simple-rtmp-server/issues/162), failed if no epoll. 0.9.222. +* v1.0, 2014-09-30, fix [#180](https://github.com/winlinvip/simple-rtmp-server/issues/180), crash for multiple edge publishing the same stream. 0.9.220. +* v1.0, 2014-09-26, fix hls bug, refine config and log, according to clion of jetbrains. 0.9.216. +* v1.0, 2014-09-25, fix [#177](https://github.com/winlinvip/simple-rtmp-server/issues/177), dvr segment add config dvr_wait_keyframe. 0.9.213. +* v1.0, 2014-08-28, fix [#167](https://github.com/winlinvip/simple-rtmp-server/issues/167), add openssl includes to utest. 0.9.209. +* v1.0, 2014-08-27, max connections is 32756, for st use mmap default. 0.9.209 +* v1.0, 2014-08-24, fix [#150](https://github.com/winlinvip/simple-rtmp-server/issues/150), forward should forward the sequence header when retry. 0.9.208. +* v1.0, 2014-08-22, for [#165](https://github.com/winlinvip/simple-rtmp-server/issues/165), refine dh wrapper, ensure public key is 128bytes. 0.9.206. +* v1.0, 2014-08-19, for [#160](https://github.com/winlinvip/simple-rtmp-server/issues/160), support forward/edge to flussonic, disable debug_srs_upnode to make flussonic happy. 0.9.201. +* v1.0, 2014-08-17, for [#155](https://github.com/winlinvip/simple-rtmp-server/issues/155), refine for osx, with ssl/http, disable statistics. 0.9.198. +* v1.0, 2014-08-06, fix [#148](https://github.com/winlinvip/simple-rtmp-server/issues/148), simplify the RTMP handshake key generation. 0.9.191. +* v1.0, 2014-08-06, fix [#147](https://github.com/winlinvip/simple-rtmp-server/issues/147), support identify the srs edge. 0.9.190. +* v1.0, 2014-08-03, [1.0 mainline7(0.9.189)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline7) released. 57432 lines. +* v1.0, 2014-08-03, fix [#79](https://github.com/winlinvip/simple-rtmp-server/issues/79), fix the reload remove edge assert bug. 0.9.189. +* v1.0, 2014-08-03, fix [#57](https://github.com/winlinvip/simple-rtmp-server/issues/57), use lock(acquire/release publish) to avoid duplicated publishing. 0.9.188. +* v1.0, 2014-08-03, fix [#85](https://github.com/winlinvip/simple-rtmp-server/issues/85), fix the segment-dvr sequence header missing. 0.9.187. +* v1.0, 2014-08-03, fix [#145](https://github.com/winlinvip/simple-rtmp-server/issues/145), refine ffmpeg log, check abitrate for libaacplus. 0.9.186. +* v1.0, 2014-08-03, fix [#143](https://github.com/winlinvip/simple-rtmp-server/issues/143), fix retrieve sys stat bug for all linux. 0.9.185. +* v1.0, 2014-08-02, fix [#138](https://github.com/winlinvip/simple-rtmp-server/issues/138), fix http hooks bug, regression bug. 0.9.184. +* v1.0, 2014-08-02, fix [#142](https://github.com/winlinvip/simple-rtmp-server/issues/142), fix tcp stat slow bug, use /proc/net/sockstat instead, refer to 'ss -s'. 0.9.183. +* v1.0, 2014-07-31, fix [#141](https://github.com/winlinvip/simple-rtmp-server/issues/141), support tun0(vpn network device) ip retrieve. 0.9.179. +* v1.0, 2014-07-27, support partially build on OSX(Darwin). 0.9.177 +* v1.0, 2014-07-27, api connections add udp, add disk iops. 0.9.176 +* v1.0, 2014-07-26, complete config utest. 0.9.173 +* v1.0, 2014-07-26, fix [#124](https://github.com/winlinvip/simple-rtmp-server/issues/124), gop cache support disable video in publishing. 0.9.171. +* v1.0, 2014-07-23, fix [#121](https://github.com/winlinvip/simple-rtmp-server/issues/121), srs_info detail log compile failed. 0.9.168. +* v1.0, 2014-07-19, fix [#119](https://github.com/winlinvip/simple-rtmp-server/issues/119), use iformat and oformat for ffmpeg transcode. 0.9.163. +* v1.0, 2014-07-13, [1.0 mainline6(0.9.160)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline6) released. 50029 lines. +* v1.0, 2014-07-13, refine the bandwidth check/test, add as/js library, use srs-librtmp for linux tool. 0.9.159 +* v1.0, 2014-07-12, complete rtmp stack utest. 0.9.156 +* v1.0, 2014-07-06, fix [#81](https://github.com/winlinvip/simple-rtmp-server/issues/81), fix HLS codec info, IOS ok. 0.9.153. +* v1.0, 2014-07-06, fix [#103](https://github.com/winlinvip/simple-rtmp-server/issues/103), support all aac sample rate. 0.9.150. +* v1.0, 2014-07-05, complete kernel utest. 0.9.149 +* v1.0, 2014-06-30, fix [#111](https://github.com/winlinvip/simple-rtmp-server/issues/111), always use 31bits timestamp. 0.9.143. +* v1.0, 2014-06-28, response the call message with null. 0.9.137 +* v1.0, 2014-06-28, fix [#110](https://github.com/winlinvip/simple-rtmp-server/issues/110), thread start segment fault, thread cycle stop destroy thread. 0.9.136 +* v1.0, 2014-06-27, fix [#109](https://github.com/winlinvip/simple-rtmp-server/issues/109), fix the system jump time, adjust system startup time. 0.9.135 +* v1.0, 2014-06-27, [1.0 mainline5(0.9.134)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline5) released. 41573 lines. +* v1.0, 2014-06-27, SRS online 30days with RTMP/HLS. +* v1.0, 2014-06-25, fix [#108](https://github.com/winlinvip/simple-rtmp-server/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133 +* v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132 +* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 +* v1.0, 2014-06-21, support edge [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse), fix [#104](https://github.com/winlinvip/simple-rtmp-server/issues/104). 0.9.129 +* v1.0, 2014-06-19, add connections count to api summaries. 0.9.127 +* v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126 +* v1.0, 2014-06-18, add network bytes to api summaries. 0.9.125 +* v1.0, 2014-06-14, fix [#98](https://github.com/winlinvip/simple-rtmp-server/issues/98), workaround for librtmp ping(fmt=1,cid=2 fresh stream). 0.9.124 +* v1.0, 2014-05-29, support flv inject and flv http streaming with start=bytes. 0.9.122 +* v1.0, 2014-05-28, [1.0 mainline4(0.9.120)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline4) released. 39200 lines. +* v1.0, 2014-05-27, fix [#87](https://github.com/winlinvip/simple-rtmp-server/issues/87), add source id for full trackable log. 0.9.120 +* v1.0, 2014-05-27, fix [#84](https://github.com/winlinvip/simple-rtmp-server/issues/84), unpublish when edge disconnect. 0.9.119 +* v1.0, 2014-05-27, fix [#89](https://github.com/winlinvip/simple-rtmp-server/issues/89), config to /dev/null to disable ffmpeg log. 0.9.117 +* v1.0, 2014-05-25, fix [#76](https://github.com/winlinvip/simple-rtmp-server/issues/76), allow edge vhost to add or remove. 0.9.114 +* v1.0, 2014-05-24, Johnny contribute [ossrs.net](http://ossrs.net). karthikeyan start to translate wiki to English. +* v1.0, 2014-05-22, fix [#78](https://github.com/winlinvip/simple-rtmp-server/issues/78), st joinable thread must be stop by other threads, 0.9.113 +* v1.0, 2014-05-22, support amf0 StrictArray(0x0a). 0.9.111. +* v1.0, 2014-05-22, support flv parser, add amf0 to librtmp. 0.9.110 +* v1.0, 2014-05-22, fix [#74](https://github.com/winlinvip/simple-rtmp-server/issues/74), add tcUrl for http callback on_connect, 0.9.109 +* v1.0, 2014-05-19, support http heartbeat, 0.9.107 +* v1.0, 2014-05-18, [1.0 mainline3(0.9.105)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline3) released. 37594 lines. +* v1.0, 2014-05-18, support http api json, to PUT/POST. 0.9.105 +* v1.0, 2014-05-17, fix [#72](https://github.com/winlinvip/simple-rtmp-server/issues/72), also need stream_id for send_and_free_message. 0.9.101 +* v1.0, 2014-05-17, rename struct to class. 0.9.100 +* v1.0, 2014-05-14, fix [#67](https://github.com/winlinvip/simple-rtmp-server/issues/67) pithy print, stage must has a age. 0.9.98 +* v1.0, 2014-05-13, fix mem leak for delete[] SharedPtrMessage array. 0.9.95 +* v1.0, 2014-05-12, refine the kbps calc module. 0.9.93 +* v1.0, 2014-05-12, fix bug [#64](https://github.com/winlinvip/simple-rtmp-server/issues/64): install_dir=DESTDIR+PREFIX +* v1.0, 2014-05-08, fix [#36](https://github.com/winlinvip/simple-rtmp-server/issues/36): never directly use \*(int32_t\*) for arm. +* v1.0, 2014-05-08, fix [#60](https://github.com/winlinvip/simple-rtmp-server/issues/60): support aggregate message +* v1.0, 2014-05-08, fix [#59](https://github.com/winlinvip/simple-rtmp-server/issues/59), edge support FMS origin server. 0.9.92 +* v1.0, 2014-05-06, fix [#50](https://github.com/winlinvip/simple-rtmp-server/issues/50), ubuntu14 build error. +* v1.0, 2014-05-04, support mips linux. +* v1.0, 2014-04-30, fix bug [#34](https://github.com/winlinvip/simple-rtmp-server/issues/34): convert signal to io thread. 0.9.85 +* v1.0, 2014-04-29, refine RTMP protocol completed, to 0.9.81 +* v1.0, 2014-04-28, [1.0 mainline2(0.9.79)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline2) released. 35255 lines. +* v1.0, 2014-04-28, support full edge RTMP server. 0.9.79 +* v1.0, 2014-04-27, support basic edge(play/publish) RTMP server. 0.9.78 +* v1.0, 2014-04-25, add donation page. 0.9.76 +* v1.0, 2014-04-21, support android app to start srs for internal edge. 0.9.72 +* v1.0, 2014-04-19, support tool over srs-librtmp to ingest flv/rtmp. 0.9.71 +* v1.0, 2014-04-17, support dvr(record live to flv file for vod). 0.9.69 +* v1.0, 2014-04-11, add speex1.2 to transcode flash encoder stream. 0.9.58 +* v1.0, 2014-04-10, support reload ingesters(add/remov/update). 0.9.57 +* v1.0, 2014-04-07, [1.0 mainline(0.9.55)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline) released. 30000 lines. +* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest) file/stream/device. +* v1.0, 2014-04-05, support [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi) and [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer). +* v1.0, 2014-04-03, implements http framework and api/v1/version. +* v1.0, 2014-03-30, fix bug for st detecting epoll failed, force st to use epoll. +* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi). +* v1.0, 2014-03-29, add release binary package for raspberry-pi. +* v1.0, 2014-03-26, support RTMP ATC for HLS/HDS to support backup(failover). +* v1.0, 2014-03-23, support daemon, default start in daemon. +* v1.0, 2014-03-22, support make install/install-api and uninstall. +* v1.0, 2014-03-22, add ./etc/init.d/srs, refine to support make clean then make. +* v1.0, 2014-03-21, write pid to ./objs/srs.pid. +* v1.0, 2014-03-20, refine hls code, support pure audio HLS. +* v1.0, 2014-03-19, add vn/an for FFMPEG to drop video/audio for radio stream. +* v1.0, 2014-03-19, refine handshake, client support complex handshake, add utest. +* v1.0, 2014-03-16, fix bug on arm of st, the sp change from 20 to 8, for respberry-pi, @see [commit](https://github.com/winlinvip/simple-rtmp-server/commit/5a4373d4835758188b9a1f03005cea0b6ddc62aa) +* v1.0, 2014-03-16, support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +* v1.0, 2014-03-12, finish utest for amf0 codec. +* v1.0, 2014-03-06, add gperftools for mem leak detect, mem/cpu profile. +* v1.0, 2014-03-04, add gest framework for utest, build success. +* v1.0, 2014-03-02, add wiki [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp), [SRS for arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), [product](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product) +* v1.0, 2014-03-02, srs-librtmp, client publish/play library like librtmp. +* v1.0, 2014-03-01, modularity, extract core/kernel/rtmp/app/main module. +* v1.0, 2014-02-28, support arm build(SRS/ST), add ssl to 3rdparty package. +* v1.0, 2014-02-28, add wiki [BuildArm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), [FFMPEG](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) +* v1.0, 2014-02-27, add wiki [LowLatency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency), [HTTPCallback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), [ServerSideScript](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_ServerSideScript), [IDE](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE) +* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) +* v1.0, 2014-01-12, add wiki [HowToAskQuestion](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HowToAskQuestion), [RtmpUrlVhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost) +* v1.0, 2014-01-11, fix jw/flower player pause bug, which send closeStream actually. +* v1.0, 2014-01-05, add wiki [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), [Performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) +* v1.0, 2014-01-01, change listen(512), chunk-size(60000), to improve performance. +* v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. +* v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. +* v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone. +* v0.9, 2013-12-22, demo video meeting or chat(SRS+cherrypy+jquery+bootstrap). +* v0.9, 2013-12-22, merge from wenjie, support banwidth test. +* v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level +* v0.9, 2013-12-21, add [players](http://demo.srs.com/players) for play and publish. +* 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 released. 19186 lines. -* v0.8, 2013-12-08, support http hooks: on_connect/close/publish/unpublish/play/stop. +* 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](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback): 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. * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. -* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use srs rtmp sdk. +* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use SRS rtmp sdk. * 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. @@ -241,12 +577,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. @@ -255,7 +591,186 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw * v0.1, 2013-10-18, support rtmp message2chunk protocol(send\_message). * v0.1, 2013-10-17, support rtmp chunk2message protocol(recv\_message). -beijing, 2013 -Winlin +## Performance + +Performance benchmark history, on virtual box: + +* 2013-11-28, SRS 0.5.0, 1800clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679) +* 2014-07-12, SRS 0.9.156, 1800clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a) +* 2014-07-12, SRS 0.9.156, 2700clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9) +* 2014-11-11, SRS 1.0.5, 2700clients, 85%CPU, 66MB. (1.0 equals 2.0.12) + +Latest benchmark(2014-07-12): + +1. 300 connections, 150Mbps, 500kbps, CPU 5.7%, MEM 9208KB. +1. 600 connections, 300Mbps, 500kbps, CPU 18.3%, MEM 13MB. +1. 900 connections, 450Mbps, 500kbps, CPU 27.9%, MEM 20MB. +1. 1200 connections, 600Mbps, 500kbps, CPU 43.9%, MEM 26MB. +1. 1500 connections, 750Mbps, 500kbps, CPU 55.2%, MEM 32MB. +1. 1800 connections, 900Mbps, 500kbps, CPU 68.8%, MEM 38MB. +1. 2100 connections, 1050Mbps, 500kbps, CPU 75.7%, MEM 46MB. +1. 2400 connections, 1200Mbps, 500kbps, CPU 83.7%, MEM 54MB. +1. 2700 connections, 1350Mbps, 500kbps, CPU 89.9%, MEM 61MB. + +
+[winlin@dev6 srs]$ dstat
+----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
+usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
+ 29  17  39   0   0  15|   0  5325B| 163M  163M|   0     0 |4331  3386 
+ 30  16  38   0   0  16|   0  5325B| 160M  160M|   0     0 |4252  3332 
+ 30  15  37   0   0  17|   0  7646B| 169M  169M|   0     0 |4015  2886 
+ 30  17  36   0   0  17|   0  1638B| 197M  197M|   0     0 |4021  3037 
+ 31  17  35   0   0  17|   0   410B| 204M  204M|   0     0 |4181  3243 
+ 33  17  32   0   0  18|   0  2185B| 191M  191M|   0     0 |4305  3592 
+ 31  15  36   0   0  18|   0  1229B| 127M  127M|   0     0 |4446  3822 
+ 34  18  30   0   0  18|   0     0 | 231M  231M|   0     0 |4461  3691 
+ 32  17  33   0   0  18|   0   410B| 169M  169M|   0     0 |4518  3788 
+
+ +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) + +## Architecture + +SRS always use the most simple architecture to support complex transaction. +* System arch: the system structure and arch. +* Modularity arch: the main modularity of SRS. +* Stream arch: the stream dispatch arch of SRS. +* RTMP cluster arch: the RTMP origin and edge cluster arch. +* Multiple processes arch (by wenjie): the multiple process of SRS. +* CLI arch: the cli arch for SRS, api to manage SRS. +* Bandwidth specification: the bandwidth test specification of SRS. + +### System Architecture + +
++------------------------------------------------------+
+|             SRS(Simple RTMP Server)                  |
++---------------+---------------+-----------+----------+
+|   API/hook    |   Transcoder  |    HLS    |   RTMP   |
+|  http-parser  |  FFMPEG/x264  |  NGINX/ts | protocol |
++---------------+---------------+-----------+----------+
+|              Network(state-threads)                  |
++------------------------------------------------------+
+|      All Linux(RHEL,CentOS,Ubuntu,Fedora...)         |
++------------------------------------------------------+
+
+ +### Modularity Architecture + +
++------------------------------------------------------+
+|             Main(srs/bandwidth/librtmp)              |
++------------------------------------------------------+
+|           App(Server/Client application)             |
++------------------------------------------------------+
+|               RTMP(Protocol stack)                   |
++------------------------------------------------------+
+|      Kernel(depends on Core, provides error/log)     |
++------------------------------------------------------+
+|         Core(depends only on system apis)            |
++------------------------------------------------------+
+
+ +### Stream Architecture + +
+                   +---------+              +----------+
+                   + Publish +              +  Deliver |
+                   +---|-----+              +----|-----+
++----------------------+-------------------------+----------------+
+|     Input            | SRS(Simple RTMP Server) |     Output     |
++----------------------+-------------------------+----------------+
+|    Encoder(1)        |   +-> RTMP protocol ----+-> Flash Player |
+|  (FMLE,FFMPEG, -rtmp-+->-+-> HLS/NGINX --------+-> m3u8 player  |
+|  Flash,XSPLIT,       |   +-> Fowarder ---------+-> RTMP Server  |
+|  ......)             |   +-> Transcoder -------+-> RTMP Server  |
+|                      |   +-> DVR --------------+-> FILE         |
+|                      |   +-> BandwidthTest ----+-> Flash/StLoad |
++----------------------+                         |                |
+|  MediaSource(2)      |                         |                |
+|  (RTSP,FILE,         |                         |                |
+|   HTTP,HLS,    ------+->-- Ingester ----(rtmp)-+-> SRS          |
+|   Device,            |                         |                |
+|   ......)            |                         |                |
++----------------------+-------------------------+----------------+
+
+Remark:
+(1) Encoder: encoder must push RTMP stream to SRS server.
+(2) MediaSource: any media source, which can be ingest by ffmpeg.
+(3) Ingester: SRS will fork a process to run ffmpeg(or your application) 
+to ingest any input to rtmp, push to SRS.
+
+### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) + +
+                        +----------+        +----------+
+               +--ATC->-+  server  +--ATC->-+ packager +-+   +---------+
++----------+   | RTMP   +----------+ RTMP   +----------+ |   | Reverse |    +-------+
+| encoder  +->-+                                         +->-+  Proxy  +-->-+  CDN  +
++----------+   |        +----------+        +----------+ |   | (nginx) |    +-------+
+               +--ATC->-+  server  +--ATC->-+ packager +-+   +---------+
+                 RTMP   +----------+ RTMP   +----------+
+
+ +### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) + +Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) +Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) + +
++---------+       +-----------------+     +-----------------------+ 
++ Encoder +--+-->-+  SRS(RTMP Edge) +--->-+     (RTMP Origin)     | 
++---------+  |    +-----------------+     |   SRS/FMS/NGINX-RTMP  |
+             |                            |    Red5/HELIX/CRTMP   |
+             +-------------------------->-+         ......        |
+                                          +-----------------------+ 
+Schema#1: Any RTMP encoder push RTMP stream to RTMP (origin/edge)server,
+    where SRS RTMP Edge server will forward stream to origin.
+
+
++-------------+    +-----------------+      +--------------------+
+| RTMP Origin +-->-+  SRS(RTMP Edge) +--+->-+  Client(RTMP/HLS)  |
++-------------+    +-----------------+  |   |  Flash/IOS/Android |
+                                        |   +--------------------+
+                                        |
+                                        |   +-----------------+
+                                        +->-+  SRS(RTMP Edge) +
+                                            +-----------------+
+Schema#2: SRS RTMP Edge server pull stream from origin (or upstream SRS 
+    RTMP Edge server), then delivery to Client.
+
+ +### Bandwidth Test Workflow + +
+   +------------+                    +----------+
+   |  Client    |                    |  Server  |
+   +-----+------+                    +-----+----+
+         |                                 |
+         |   connect vhost------------->   |
+         |   <-----------result(success)   |
+         |                                 |
+         |   <----------call(start play)   |
+         |   result(playing)---------->    |
+         |   <-------------data(playing)   |
+         |   <-----------call(stop play)   |
+         |   result(stopped)---------->    |
+         |                                 |
+         |   <-------call(start publish)   |
+         |   result(publishing)------->    |
+         |   data(publishing)--------->    |
+         |   <--------call(stop publish)   |
+         |   result(stopped)(1)------->    |
+         |                                 |
+         |   <--------------------report   |
+         |   final(2)----------------->    |
+         |           <END>                 |
+         
+@See: class SrsBandwidth comments.
+
+ +Beijing, 2013.10
+Winlin diff --git a/trunk/3rdparty/bootstrap.2.3.2.zip b/trunk/3rdparty/bootstrap.2.3.2.zip new file mode 100644 index 00000000..47536ba2 Binary files /dev/null and b/trunk/3rdparty/bootstrap.2.3.2.zip differ diff --git a/trunk/3rdparty/tools/build_ccache.sh b/trunk/3rdparty/ccache/build_ccache.sh similarity index 100% rename from trunk/3rdparty/tools/build_ccache.sh rename to trunk/3rdparty/ccache/build_ccache.sh diff --git a/trunk/3rdparty/tools/ccache-3.1.9.zip b/trunk/3rdparty/ccache/ccache-3.1.9.zip similarity index 100% rename from trunk/3rdparty/tools/ccache-3.1.9.zip rename to trunk/3rdparty/ccache/ccache-3.1.9.zip diff --git a/trunk/3rdparty/ccache/readme.txt b/trunk/3rdparty/ccache/readme.txt new file mode 100644 index 00000000..611d9eae --- /dev/null +++ b/trunk/3rdparty/ccache/readme.txt @@ -0,0 +1,11 @@ +ccache是samba组织提供的加速编译过程的工具, +使用虚拟机编译可以考虑用这个工具,让编译过程飞快。 + +链接: + http://ccache.samba.org/ + http://samba.org/ftp/ccache/ccache-3.1.9.tar.xz + http://ccache.samba.org/manual.html + +安装方法: + bash build_ccache.sh +注意:要求以sudoer执行,要修改文件。 \ No newline at end of file diff --git a/trunk/3rdparty/gperftools-2.1.zip b/trunk/3rdparty/gperftools-2.1.zip new file mode 100644 index 00000000..49a01918 Binary files /dev/null and b/trunk/3rdparty/gperftools-2.1.zip differ diff --git a/trunk/3rdparty/gprof/build_gprof2dot.sh b/trunk/3rdparty/gprof/build_gprof2dot.sh new file mode 100755 index 00000000..c8a53ff6 --- /dev/null +++ b/trunk/3rdparty/gprof/build_gprof2dot.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# check exists. +if [[ -d graphviz-2.18 ]]; then + echo "graphviz is ok"; + exit 0; +fi + +# check sudoer. +sudo echo "ok" > /dev/null 2>&1; +ret=$?; if [[ 0 -ne ${ret} ]]; then echo "you must be sudoer"; exit 1; fi + +unzip -q graphviz-2.36.0.zip +cd graphviz-2.36.0 && ./configure && make && sudo make install +ret=$?; if [[ $ret -ne 0 ]]; then echo "build gprof2dot failed."; exit $ret; fi + +echo "we test in Centos6.0, it's ok" diff --git a/trunk/3rdparty/gprof/gprof2dot.py b/trunk/3rdparty/gprof/gprof2dot.py new file mode 100755 index 00000000..55eb53ad --- /dev/null +++ b/trunk/3rdparty/gprof/gprof2dot.py @@ -0,0 +1,2227 @@ +#!/usr/bin/env python +# +# Copyright 2008-2009 Jose Fonseca +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +"""Generate a dot graph from the output of several profilers.""" + +__author__ = "Jose Fonseca" + +__version__ = "1.0" + + +import sys +import math +import os.path +import re +import textwrap +import optparse +import xml.parsers.expat + + +try: + # Debugging helper module + import debug +except ImportError: + pass + + +def percentage(p): + return "%.02f%%" % (p*100.0,) + +def add(a, b): + return a + b + +def equal(a, b): + if a == b: + return a + else: + return None + +def fail(a, b): + assert False + + +tol = 2 ** -23 + +def ratio(numerator, denominator): + try: + ratio = float(numerator)/float(denominator) + except ZeroDivisionError: + # 0/0 is undefined, but 1.0 yields more useful results + return 1.0 + if ratio < 0.0: + if ratio < -tol: + sys.stderr.write('warning: negative ratio (%s/%s)\n' % (numerator, denominator)) + return 0.0 + if ratio > 1.0: + if ratio > 1.0 + tol: + sys.stderr.write('warning: ratio greater than one (%s/%s)\n' % (numerator, denominator)) + return 1.0 + return ratio + + +class UndefinedEvent(Exception): + """Raised when attempting to get an event which is undefined.""" + + def __init__(self, event): + Exception.__init__(self) + self.event = event + + def __str__(self): + return 'unspecified event %s' % self.event.name + + +class Event(object): + """Describe a kind of event, and its basic operations.""" + + def __init__(self, name, null, aggregator, formatter = str): + self.name = name + self._null = null + self._aggregator = aggregator + self._formatter = formatter + + def __eq__(self, other): + return self is other + + def __hash__(self): + return id(self) + + def null(self): + return self._null + + def aggregate(self, val1, val2): + """Aggregate two event values.""" + assert val1 is not None + assert val2 is not None + return self._aggregator(val1, val2) + + def format(self, val): + """Format an event value.""" + assert val is not None + return self._formatter(val) + + +MODULE = Event("Module", None, equal) +PROCESS = Event("Process", None, equal) + +CALLS = Event("Calls", 0, add) +SAMPLES = Event("Samples", 0, add) +SAMPLES2 = Event("Samples", 0, add) + +TIME = Event("Time", 0.0, add, lambda x: '(' + str(x) + ')') +TIME_RATIO = Event("Time ratio", 0.0, add, lambda x: '(' + percentage(x) + ')') +TOTAL_TIME = Event("Total time", 0.0, fail) +TOTAL_TIME_RATIO = Event("Total time ratio", 0.0, fail, percentage) + +CALL_RATIO = Event("Call ratio", 0.0, add, percentage) + +PRUNE_RATIO = Event("Prune ratio", 0.0, add, percentage) + + +class Object(object): + """Base class for all objects in profile which can store events.""" + + def __init__(self, events=None): + if events is None: + self.events = {} + else: + self.events = events + + def __hash__(self): + return id(self) + + def __eq__(self, other): + return self is other + + def __contains__(self, event): + return event in self.events + + def __getitem__(self, event): + try: + return self.events[event] + except KeyError: + raise UndefinedEvent(event) + + def __setitem__(self, event, value): + if value is None: + if event in self.events: + del self.events[event] + else: + self.events[event] = value + + +class Call(Object): + """A call between functions. + + There should be at most one call object for every pair of functions. + """ + + def __init__(self, callee_id): + Object.__init__(self) + self.callee_id = callee_id + + +class Function(Object): + """A function.""" + + def __init__(self, id, name): + Object.__init__(self) + self.id = id + self.name = name + self.calls = {} + self.cycle = None + + def add_call(self, call): + if call.callee_id in self.calls: + sys.stderr.write('warning: overwriting call from function %s to %s\n' % (str(self.id), str(call.callee_id))) + self.calls[call.callee_id] = call + + # TODO: write utility functions + + def __repr__(self): + return self.name + + +class Cycle(Object): + """A cycle made from recursive function calls.""" + + def __init__(self): + Object.__init__(self) + # XXX: Do cycles need an id? + self.functions = set() + + def add_function(self, function): + assert function not in self.functions + self.functions.add(function) + # XXX: Aggregate events? + if function.cycle is not None: + for other in function.cycle.functions: + if function not in self.functions: + self.add_function(other) + function.cycle = self + + +class Profile(Object): + """The whole profile.""" + + def __init__(self): + Object.__init__(self) + self.functions = {} + self.cycles = [] + + def add_function(self, function): + if function.id in self.functions: + sys.stderr.write('warning: overwriting function %s (id %s)\n' % (function.name, str(function.id))) + self.functions[function.id] = function + + def add_cycle(self, cycle): + self.cycles.append(cycle) + + def validate(self): + """Validate the edges.""" + + for function in self.functions.itervalues(): + for callee_id in function.calls.keys(): + assert function.calls[callee_id].callee_id == callee_id + if callee_id not in self.functions: + sys.stderr.write('warning: call to undefined function %s from function %s\n' % (str(callee_id), function.name)) + del function.calls[callee_id] + + def find_cycles(self): + """Find cycles using Tarjan's strongly connected components algorithm.""" + + # Apply the Tarjan's algorithm successively until all functions are visited + visited = set() + for function in self.functions.itervalues(): + if function not in visited: + self._tarjan(function, 0, [], {}, {}, visited) + cycles = [] + for function in self.functions.itervalues(): + if function.cycle is not None and function.cycle not in cycles: + cycles.append(function.cycle) + self.cycles = cycles + if 0: + for cycle in cycles: + sys.stderr.write("Cycle:\n") + for member in cycle.functions: + sys.stderr.write("\tFunction %s\n" % member.name) + + def _tarjan(self, function, order, stack, orders, lowlinks, visited): + """Tarjan's strongly connected components algorithm. + + See also: + - http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm + """ + + visited.add(function) + orders[function] = order + lowlinks[function] = order + order += 1 + pos = len(stack) + stack.append(function) + for call in function.calls.itervalues(): + callee = self.functions[call.callee_id] + # TODO: use a set to optimize lookup + if callee not in orders: + order = self._tarjan(callee, order, stack, orders, lowlinks, visited) + lowlinks[function] = min(lowlinks[function], lowlinks[callee]) + elif callee in stack: + lowlinks[function] = min(lowlinks[function], orders[callee]) + if lowlinks[function] == orders[function]: + # Strongly connected component found + members = stack[pos:] + del stack[pos:] + if len(members) > 1: + cycle = Cycle() + for member in members: + cycle.add_function(member) + return order + + def call_ratios(self, event): + # Aggregate for incoming calls + cycle_totals = {} + for cycle in self.cycles: + cycle_totals[cycle] = 0.0 + function_totals = {} + for function in self.functions.itervalues(): + function_totals[function] = 0.0 + for function in self.functions.itervalues(): + for call in function.calls.itervalues(): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + function_totals[callee] += call[event] + if callee.cycle is not None and callee.cycle is not function.cycle: + cycle_totals[callee.cycle] += call[event] + + # Compute the ratios + for function in self.functions.itervalues(): + for call in function.calls.itervalues(): + assert CALL_RATIO not in call + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is not None and callee.cycle is not function.cycle: + total = cycle_totals[callee.cycle] + else: + total = function_totals[callee] + call[CALL_RATIO] = ratio(call[event], total) + + def integrate(self, outevent, inevent): + """Propagate function time ratio allong the function calls. + + Must be called after finding the cycles. + + See also: + - http://citeseer.ist.psu.edu/graham82gprof.html + """ + + # Sanity checking + assert outevent not in self + for function in self.functions.itervalues(): + assert outevent not in function + assert inevent in function + for call in function.calls.itervalues(): + assert outevent not in call + if call.callee_id != function.id: + assert CALL_RATIO in call + + # Aggregate the input for each cycle + for cycle in self.cycles: + total = inevent.null() + for function in self.functions.itervalues(): + total = inevent.aggregate(total, function[inevent]) + self[inevent] = total + + # Integrate along the edges + total = inevent.null() + for function in self.functions.itervalues(): + total = inevent.aggregate(total, function[inevent]) + self._integrate_function(function, outevent, inevent) + self[outevent] = total + + def _integrate_function(self, function, outevent, inevent): + if function.cycle is not None: + return self._integrate_cycle(function.cycle, outevent, inevent) + else: + if outevent not in function: + total = function[inevent] + for call in function.calls.itervalues(): + if call.callee_id != function.id: + total += self._integrate_call(call, outevent, inevent) + function[outevent] = total + return function[outevent] + + def _integrate_call(self, call, outevent, inevent): + assert outevent not in call + assert CALL_RATIO in call + callee = self.functions[call.callee_id] + subtotal = call[CALL_RATIO]*self._integrate_function(callee, outevent, inevent) + call[outevent] = subtotal + return subtotal + + def _integrate_cycle(self, cycle, outevent, inevent): + if outevent not in cycle: + + # Compute the outevent for the whole cycle + total = inevent.null() + for member in cycle.functions: + subtotal = member[inevent] + for call in member.calls.itervalues(): + callee = self.functions[call.callee_id] + if callee.cycle is not cycle: + subtotal += self._integrate_call(call, outevent, inevent) + total += subtotal + cycle[outevent] = total + + # Compute the time propagated to callers of this cycle + callees = {} + for function in self.functions.itervalues(): + if function.cycle is not cycle: + for call in function.calls.itervalues(): + callee = self.functions[call.callee_id] + if callee.cycle is cycle: + try: + callees[callee] += call[CALL_RATIO] + except KeyError: + callees[callee] = call[CALL_RATIO] + + for member in cycle.functions: + member[outevent] = outevent.null() + + for callee, call_ratio in callees.iteritems(): + ranks = {} + call_ratios = {} + partials = {} + self._rank_cycle_function(cycle, callee, 0, ranks) + self._call_ratios_cycle(cycle, callee, ranks, call_ratios, set()) + partial = self._integrate_cycle_function(cycle, callee, call_ratio, partials, ranks, call_ratios, outevent, inevent) + assert partial == max(partials.values()) + assert not total or abs(1.0 - partial/(call_ratio*total)) <= 0.001 + + return cycle[outevent] + + def _rank_cycle_function(self, cycle, function, rank, ranks): + if function not in ranks or ranks[function] > rank: + ranks[function] = rank + for call in function.calls.itervalues(): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is cycle: + self._rank_cycle_function(cycle, callee, rank + 1, ranks) + + def _call_ratios_cycle(self, cycle, function, ranks, call_ratios, visited): + if function not in visited: + visited.add(function) + for call in function.calls.itervalues(): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is cycle: + if ranks[callee] > ranks[function]: + call_ratios[callee] = call_ratios.get(callee, 0.0) + call[CALL_RATIO] + self._call_ratios_cycle(cycle, callee, ranks, call_ratios, visited) + + def _integrate_cycle_function(self, cycle, function, partial_ratio, partials, ranks, call_ratios, outevent, inevent): + if function not in partials: + partial = partial_ratio*function[inevent] + for call in function.calls.itervalues(): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is not cycle: + assert outevent in call + partial += partial_ratio*call[outevent] + else: + if ranks[callee] > ranks[function]: + callee_partial = self._integrate_cycle_function(cycle, callee, partial_ratio, partials, ranks, call_ratios, outevent, inevent) + call_ratio = ratio(call[CALL_RATIO], call_ratios[callee]) + call_partial = call_ratio*callee_partial + try: + call[outevent] += call_partial + except UndefinedEvent: + call[outevent] = call_partial + partial += call_partial + partials[function] = partial + try: + function[outevent] += partial + except UndefinedEvent: + function[outevent] = partial + return partials[function] + + def aggregate(self, event): + """Aggregate an event for the whole profile.""" + + total = event.null() + for function in self.functions.itervalues(): + try: + total = event.aggregate(total, function[event]) + except UndefinedEvent: + return + self[event] = total + + def ratio(self, outevent, inevent): + assert outevent not in self + assert inevent in self + for function in self.functions.itervalues(): + assert outevent not in function + assert inevent in function + function[outevent] = ratio(function[inevent], self[inevent]) + for call in function.calls.itervalues(): + assert outevent not in call + if inevent in call: + call[outevent] = ratio(call[inevent], self[inevent]) + self[outevent] = 1.0 + + def prune(self, node_thres, edge_thres): + """Prune the profile""" + + # compute the prune ratios + for function in self.functions.itervalues(): + try: + function[PRUNE_RATIO] = function[TOTAL_TIME_RATIO] + except UndefinedEvent: + pass + + for call in function.calls.itervalues(): + callee = self.functions[call.callee_id] + + if TOTAL_TIME_RATIO in call: + # handle exact cases first + call[PRUNE_RATIO] = call[TOTAL_TIME_RATIO] + else: + try: + # make a safe estimate + call[PRUNE_RATIO] = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO]) + except UndefinedEvent: + pass + + # prune the nodes + for function_id in self.functions.keys(): + function = self.functions[function_id] + try: + if function[PRUNE_RATIO] < node_thres: + del self.functions[function_id] + except UndefinedEvent: + pass + + # prune the egdes + for function in self.functions.itervalues(): + for callee_id in function.calls.keys(): + call = function.calls[callee_id] + try: + if callee_id not in self.functions or call[PRUNE_RATIO] < edge_thres: + del function.calls[callee_id] + except UndefinedEvent: + pass + + def dump(self): + for function in self.functions.itervalues(): + sys.stderr.write('Function %s:\n' % (function.name,)) + self._dump_events(function.events) + for call in function.calls.itervalues(): + callee = self.functions[call.callee_id] + sys.stderr.write(' Call %s:\n' % (callee.name,)) + self._dump_events(call.events) + for cycle in self.cycles: + sys.stderr.write('Cycle:\n') + self._dump_events(cycle.events) + for function in cycle.functions: + sys.stderr.write(' Function %s\n' % (function.name,)) + + def _dump_events(self, events): + for event, value in events.iteritems(): + sys.stderr.write(' %s: %s\n' % (event.name, event.format(value))) + + +class Struct: + """Masquerade a dictionary with a structure-like behavior.""" + + def __init__(self, attrs = None): + if attrs is None: + attrs = {} + self.__dict__['_attrs'] = attrs + + def __getattr__(self, name): + try: + return self._attrs[name] + except KeyError: + raise AttributeError(name) + + def __setattr__(self, name, value): + self._attrs[name] = value + + def __str__(self): + return str(self._attrs) + + def __repr__(self): + return repr(self._attrs) + + +class ParseError(Exception): + """Raised when parsing to signal mismatches.""" + + def __init__(self, msg, line): + self.msg = msg + # TODO: store more source line information + self.line = line + + def __str__(self): + return '%s: %r' % (self.msg, self.line) + + +class Parser: + """Parser interface.""" + + def __init__(self): + pass + + def parse(self): + raise NotImplementedError + + +class LineParser(Parser): + """Base class for parsers that read line-based formats.""" + + def __init__(self, file): + Parser.__init__(self) + self._file = file + self.__line = None + self.__eof = False + + def readline(self): + line = self._file.readline() + if not line: + self.__line = '' + self.__eof = True + self.__line = line.rstrip('\r\n') + + def lookahead(self): + assert self.__line is not None + return self.__line + + def consume(self): + assert self.__line is not None + line = self.__line + self.readline() + return line + + def eof(self): + assert self.__line is not None + return self.__eof + + +XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF = range(4) + + +class XmlToken: + + def __init__(self, type, name_or_data, attrs = None, line = None, column = None): + assert type in (XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF) + self.type = type + self.name_or_data = name_or_data + self.attrs = attrs + self.line = line + self.column = column + + def __str__(self): + if self.type == XML_ELEMENT_START: + return '<' + self.name_or_data + ' ...>' + if self.type == XML_ELEMENT_END: + return '' + if self.type == XML_CHARACTER_DATA: + return self.name_or_data + if self.type == XML_EOF: + return 'end of file' + assert 0 + + +class XmlTokenizer: + """Expat based XML tokenizer.""" + + def __init__(self, fp, skip_ws = True): + self.fp = fp + self.tokens = [] + self.index = 0 + self.final = False + self.skip_ws = skip_ws + + self.character_pos = 0, 0 + self.character_data = '' + + self.parser = xml.parsers.expat.ParserCreate() + self.parser.StartElementHandler = self.handle_element_start + self.parser.EndElementHandler = self.handle_element_end + self.parser.CharacterDataHandler = self.handle_character_data + + def handle_element_start(self, name, attributes): + self.finish_character_data() + line, column = self.pos() + token = XmlToken(XML_ELEMENT_START, name, attributes, line, column) + self.tokens.append(token) + + def handle_element_end(self, name): + self.finish_character_data() + line, column = self.pos() + token = XmlToken(XML_ELEMENT_END, name, None, line, column) + self.tokens.append(token) + + def handle_character_data(self, data): + if not self.character_data: + self.character_pos = self.pos() + self.character_data += data + + def finish_character_data(self): + if self.character_data: + if not self.skip_ws or not self.character_data.isspace(): + line, column = self.character_pos + token = XmlToken(XML_CHARACTER_DATA, self.character_data, None, line, column) + self.tokens.append(token) + self.character_data = '' + + def next(self): + size = 16*1024 + while self.index >= len(self.tokens) and not self.final: + self.tokens = [] + self.index = 0 + data = self.fp.read(size) + self.final = len(data) < size + try: + self.parser.Parse(data, self.final) + except xml.parsers.expat.ExpatError, e: + #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: + if e.code == 3: + pass + else: + raise e + if self.index >= len(self.tokens): + line, column = self.pos() + token = XmlToken(XML_EOF, None, None, line, column) + else: + token = self.tokens[self.index] + self.index += 1 + return token + + def pos(self): + return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber + + +class XmlTokenMismatch(Exception): + + def __init__(self, expected, found): + self.expected = expected + self.found = found + + def __str__(self): + return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found)) + + +class XmlParser(Parser): + """Base XML document parser.""" + + def __init__(self, fp): + Parser.__init__(self) + self.tokenizer = XmlTokenizer(fp) + self.consume() + + def consume(self): + self.token = self.tokenizer.next() + + def match_element_start(self, name): + return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name + + def match_element_end(self, name): + return self.token.type == XML_ELEMENT_END and self.token.name_or_data == name + + def element_start(self, name): + while self.token.type == XML_CHARACTER_DATA: + self.consume() + if self.token.type != XML_ELEMENT_START: + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token) + if self.token.name_or_data != name: + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token) + attrs = self.token.attrs + self.consume() + return attrs + + def element_end(self, name): + while self.token.type == XML_CHARACTER_DATA: + self.consume() + if self.token.type != XML_ELEMENT_END: + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token) + if self.token.name_or_data != name: + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token) + self.consume() + + def character_data(self, strip = True): + data = '' + while self.token.type == XML_CHARACTER_DATA: + data += self.token.name_or_data + self.consume() + if strip: + data = data.strip() + return data + + +class GprofParser(Parser): + """Parser for GNU gprof output. + + See also: + - Chapter "Interpreting gprof's Output" from the GNU gprof manual + http://sourceware.org/binutils/docs-2.18/gprof/Call-Graph.html#Call-Graph + - File "cg_print.c" from the GNU gprof source code + http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/src/gprof/cg_print.c?rev=1.12&cvsroot=src + """ + + def __init__(self, fp): + Parser.__init__(self) + self.fp = fp + self.functions = {} + self.cycles = {} + + def readline(self): + line = self.fp.readline() + if not line: + sys.stderr.write('error: unexpected end of file\n') + sys.exit(1) + line = line.rstrip('\r\n') + return line + + _int_re = re.compile(r'^\d+$') + _float_re = re.compile(r'^\d+\.\d+$') + + def translate(self, mo): + """Extract a structure from a match object, while translating the types in the process.""" + attrs = {} + groupdict = mo.groupdict() + for name, value in groupdict.iteritems(): + if value is None: + value = None + elif self._int_re.match(value): + value = int(value) + elif self._float_re.match(value): + value = float(value) + attrs[name] = (value) + return Struct(attrs) + + _cg_header_re = re.compile( + # original gprof header + r'^\s+called/total\s+parents\s*$|' + + r'^index\s+%time\s+self\s+descendents\s+called\+self\s+name\s+index\s*$|' + + r'^\s+called/total\s+children\s*$|' + + # GNU gprof header + r'^index\s+%\s+time\s+self\s+children\s+called\s+name\s*$' + ) + + _cg_ignore_re = re.compile( + # spontaneous + r'^\s+\s*$|' + # internal calls (such as "mcount") + r'^.*\((\d+)\)$' + ) + + _cg_primary_re = re.compile( + r'^\[(?P\d+)\]?' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?:(?P\d+)(?:\+(?P\d+))?)?' + + r'\s+(?P\S.*?)' + + r'(?:\s+\d+)>)?' + + r'\s\[(\d+)\]$' + ) + + _cg_parent_re = re.compile( + r'^\s+(?P\d+\.\d+)?' + + r'\s+(?P\d+\.\d+)?' + + r'\s+(?P\d+)(?:/(?P\d+))?' + + r'\s+(?P\S.*?)' + + r'(?:\s+\d+)>)?' + + r'\s\[(?P\d+)\]$' + ) + + _cg_child_re = _cg_parent_re + + _cg_cycle_header_re = re.compile( + r'^\[(?P\d+)\]?' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?P\d+\.\d+)' + + r'\s+(?:(?P\d+)(?:\+(?P\d+))?)?' + + r'\s+\d+)\sas\sa\swhole>' + + r'\s\[(\d+)\]$' + ) + + _cg_cycle_member_re = re.compile( + r'^\s+(?P\d+\.\d+)?' + + r'\s+(?P\d+\.\d+)?' + + r'\s+(?P\d+)(?:\+(?P\d+))?' + + r'\s+(?P\S.*?)' + + r'(?:\s+\d+)>)?' + + r'\s\[(?P\d+)\]$' + ) + + _cg_sep_re = re.compile(r'^--+$') + + def parse_function_entry(self, lines): + parents = [] + children = [] + + while True: + if not lines: + sys.stderr.write('warning: unexpected end of entry\n') + line = lines.pop(0) + if line.startswith('['): + break + + # read function parent line + mo = self._cg_parent_re.match(line) + if not mo: + if self._cg_ignore_re.match(line): + continue + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) + else: + parent = self.translate(mo) + parents.append(parent) + + # read primary line + mo = self._cg_primary_re.match(line) + if not mo: + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) + return + else: + function = self.translate(mo) + + while lines: + line = lines.pop(0) + + # read function subroutine line + mo = self._cg_child_re.match(line) + if not mo: + if self._cg_ignore_re.match(line): + continue + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) + else: + child = self.translate(mo) + children.append(child) + + function.parents = parents + function.children = children + + self.functions[function.index] = function + + def parse_cycle_entry(self, lines): + + # read cycle header line + line = lines[0] + mo = self._cg_cycle_header_re.match(line) + if not mo: + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) + return + cycle = self.translate(mo) + + # read cycle member lines + cycle.functions = [] + for line in lines[1:]: + mo = self._cg_cycle_member_re.match(line) + if not mo: + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) + continue + call = self.translate(mo) + cycle.functions.append(call) + + self.cycles[cycle.cycle] = cycle + + def parse_cg_entry(self, lines): + if lines[0].startswith("["): + self.parse_cycle_entry(lines) + else: + self.parse_function_entry(lines) + + def parse_cg(self): + """Parse the call graph.""" + + # skip call graph header + while not self._cg_header_re.match(self.readline()): + pass + line = self.readline() + while self._cg_header_re.match(line): + line = self.readline() + + # process call graph entries + entry_lines = [] + while line != '\014': # form feed + if line and not line.isspace(): + if self._cg_sep_re.match(line): + self.parse_cg_entry(entry_lines) + entry_lines = [] + else: + entry_lines.append(line) + line = self.readline() + + def parse(self): + self.parse_cg() + self.fp.close() + + profile = Profile() + profile[TIME] = 0.0 + + cycles = {} + for index in self.cycles.iterkeys(): + cycles[index] = Cycle() + + for entry in self.functions.itervalues(): + # populate the function + function = Function(entry.index, entry.name) + function[TIME] = entry.self + if entry.called is not None: + function[CALLS] = entry.called + if entry.called_self is not None: + call = Call(entry.index) + call[CALLS] = entry.called_self + function[CALLS] += entry.called_self + + # populate the function calls + for child in entry.children: + call = Call(child.index) + + assert child.called is not None + call[CALLS] = child.called + + if child.index not in self.functions: + # NOTE: functions that were never called but were discovered by gprof's + # static call graph analysis dont have a call graph entry so we need + # to add them here + missing = Function(child.index, child.name) + function[TIME] = 0.0 + function[CALLS] = 0 + profile.add_function(missing) + + function.add_call(call) + + profile.add_function(function) + + if entry.cycle is not None: + try: + cycle = cycles[entry.cycle] + except KeyError: + sys.stderr.write('warning: entry missing\n' % entry.cycle) + cycle = Cycle() + cycles[entry.cycle] = cycle + cycle.add_function(function) + + profile[TIME] = profile[TIME] + function[TIME] + + for cycle in cycles.itervalues(): + profile.add_cycle(cycle) + + # Compute derived events + profile.validate() + profile.ratio(TIME_RATIO, TIME) + profile.call_ratios(CALLS) + profile.integrate(TOTAL_TIME, TIME) + profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) + + return profile + + +class OprofileParser(LineParser): + """Parser for oprofile callgraph output. + + See also: + - http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph + """ + + _fields_re = { + 'samples': r'(?P\d+)', + '%': r'(?P\S+)', + 'linenr info': r'(?P\(no location information\)|\S+:\d+)', + 'image name': r'(?P\S+(?:\s\(tgid:[^)]*\))?)', + 'app name': r'(?P\S+)', + 'symbol name': r'(?P\(no symbols\)|.+?)', + } + + def __init__(self, infile): + LineParser.__init__(self, infile) + self.entries = {} + self.entry_re = None + + def add_entry(self, callers, function, callees): + try: + entry = self.entries[function.id] + except KeyError: + self.entries[function.id] = (callers, function, callees) + else: + callers_total, function_total, callees_total = entry + self.update_subentries_dict(callers_total, callers) + function_total.samples += function.samples + self.update_subentries_dict(callees_total, callees) + + def update_subentries_dict(self, totals, partials): + for partial in partials.itervalues(): + try: + total = totals[partial.id] + except KeyError: + totals[partial.id] = partial + else: + total.samples += partial.samples + + def parse(self): + # read lookahead + self.readline() + + self.parse_header() + while self.lookahead(): + self.parse_entry() + + profile = Profile() + + reverse_call_samples = {} + + # populate the profile + profile[SAMPLES] = 0 + for _callers, _function, _callees in self.entries.itervalues(): + function = Function(_function.id, _function.name) + function[SAMPLES] = _function.samples + profile.add_function(function) + profile[SAMPLES] += _function.samples + + if _function.application: + function[PROCESS] = os.path.basename(_function.application) + if _function.image: + function[MODULE] = os.path.basename(_function.image) + + total_callee_samples = 0 + for _callee in _callees.itervalues(): + total_callee_samples += _callee.samples + + for _callee in _callees.itervalues(): + if not _callee.self: + call = Call(_callee.id) + call[SAMPLES2] = _callee.samples + function.add_call(call) + + # compute derived data + profile.validate() + profile.find_cycles() + profile.ratio(TIME_RATIO, SAMPLES) + profile.call_ratios(SAMPLES2) + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) + + return profile + + def parse_header(self): + while not self.match_header(): + self.consume() + line = self.lookahead() + fields = re.split(r'\s\s+', line) + entry_re = r'^\s*' + r'\s+'.join([self._fields_re[field] for field in fields]) + r'(?P\s+\[self\])?$' + self.entry_re = re.compile(entry_re) + self.skip_separator() + + def parse_entry(self): + callers = self.parse_subentries() + if self.match_primary(): + function = self.parse_subentry() + if function is not None: + callees = self.parse_subentries() + self.add_entry(callers, function, callees) + self.skip_separator() + + def parse_subentries(self): + subentries = {} + while self.match_secondary(): + subentry = self.parse_subentry() + subentries[subentry.id] = subentry + return subentries + + def parse_subentry(self): + entry = Struct() + line = self.consume() + mo = self.entry_re.match(line) + if not mo: + raise ParseError('failed to parse', line) + fields = mo.groupdict() + entry.samples = int(fields.get('samples', 0)) + entry.percentage = float(fields.get('percentage', 0.0)) + if 'source' in fields and fields['source'] != '(no location information)': + source = fields['source'] + filename, lineno = source.split(':') + entry.filename = filename + entry.lineno = int(lineno) + else: + source = '' + entry.filename = None + entry.lineno = None + entry.image = fields.get('image', '') + entry.application = fields.get('application', '') + if 'symbol' in fields and fields['symbol'] != '(no symbols)': + entry.symbol = fields['symbol'] + else: + entry.symbol = '' + if entry.symbol.startswith('"') and entry.symbol.endswith('"'): + entry.symbol = entry.symbol[1:-1] + entry.id = ':'.join((entry.application, entry.image, source, entry.symbol)) + entry.self = fields.get('self', None) != None + if entry.self: + entry.id += ':self' + if entry.symbol: + entry.name = entry.symbol + else: + entry.name = entry.image + return entry + + def skip_separator(self): + while not self.match_separator(): + self.consume() + self.consume() + + def match_header(self): + line = self.lookahead() + return line.startswith('samples') + + def match_separator(self): + line = self.lookahead() + return line == '-'*len(line) + + def match_primary(self): + line = self.lookahead() + return not line[:1].isspace() + + def match_secondary(self): + line = self.lookahead() + return line[:1].isspace() + + +class SysprofParser(XmlParser): + + def __init__(self, stream): + XmlParser.__init__(self, stream) + + def parse(self): + objects = {} + nodes = {} + + self.element_start('profile') + while self.token.type == XML_ELEMENT_START: + if self.token.name_or_data == 'objects': + assert not objects + objects = self.parse_items('objects') + elif self.token.name_or_data == 'nodes': + assert not nodes + nodes = self.parse_items('nodes') + else: + self.parse_value(self.token.name_or_data) + self.element_end('profile') + + return self.build_profile(objects, nodes) + + def parse_items(self, name): + assert name[-1] == 's' + items = {} + self.element_start(name) + while self.token.type == XML_ELEMENT_START: + id, values = self.parse_item(name[:-1]) + assert id not in items + items[id] = values + self.element_end(name) + return items + + def parse_item(self, name): + attrs = self.element_start(name) + id = int(attrs['id']) + values = self.parse_values() + self.element_end(name) + return id, values + + def parse_values(self): + values = {} + while self.token.type == XML_ELEMENT_START: + name = self.token.name_or_data + value = self.parse_value(name) + assert name not in values + values[name] = value + return values + + def parse_value(self, tag): + self.element_start(tag) + value = self.character_data() + self.element_end(tag) + if value.isdigit(): + return int(value) + if value.startswith('"') and value.endswith('"'): + return value[1:-1] + return value + + def build_profile(self, objects, nodes): + profile = Profile() + + profile[SAMPLES] = 0 + for id, object in objects.iteritems(): + # Ignore fake objects (process names, modules, "Everything", "kernel", etc.) + if object['self'] == 0: + continue + + function = Function(id, object['name']) + function[SAMPLES] = object['self'] + profile.add_function(function) + profile[SAMPLES] += function[SAMPLES] + + for id, node in nodes.iteritems(): + # Ignore fake calls + if node['self'] == 0: + continue + + # Find a non-ignored parent + parent_id = node['parent'] + while parent_id != 0: + parent = nodes[parent_id] + caller_id = parent['object'] + if objects[caller_id]['self'] != 0: + break + parent_id = parent['parent'] + if parent_id == 0: + continue + + callee_id = node['object'] + + assert objects[caller_id]['self'] + assert objects[callee_id]['self'] + + function = profile.functions[caller_id] + + samples = node['self'] + try: + call = function.calls[callee_id] + except KeyError: + call = Call(callee_id) + call[SAMPLES2] = samples + function.add_call(call) + else: + call[SAMPLES2] += samples + + # Compute derived events + profile.validate() + profile.find_cycles() + profile.ratio(TIME_RATIO, SAMPLES) + profile.call_ratios(SAMPLES2) + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) + + return profile + + +class SharkParser(LineParser): + """Parser for MacOSX Shark output. + + Author: tom@dbservice.com + """ + + def __init__(self, infile): + LineParser.__init__(self, infile) + self.stack = [] + self.entries = {} + + def add_entry(self, function): + try: + entry = self.entries[function.id] + except KeyError: + self.entries[function.id] = (function, { }) + else: + function_total, callees_total = entry + function_total.samples += function.samples + + def add_callee(self, function, callee): + func, callees = self.entries[function.id] + try: + entry = callees[callee.id] + except KeyError: + callees[callee.id] = callee + else: + entry.samples += callee.samples + + def parse(self): + self.readline() + self.readline() + self.readline() + self.readline() + + match = re.compile(r'(?P[|+ ]*)(?P\d+), (?P[^,]+), (?P.*)') + + while self.lookahead(): + line = self.consume() + mo = match.match(line) + if not mo: + raise ParseError('failed to parse', line) + + fields = mo.groupdict() + prefix = len(fields.get('prefix', 0)) / 2 - 1 + + symbol = str(fields.get('symbol', 0)) + image = str(fields.get('image', 0)) + + entry = Struct() + entry.id = ':'.join([symbol, image]) + entry.samples = int(fields.get('samples', 0)) + + entry.name = symbol + entry.image = image + + # adjust the callstack + if prefix < len(self.stack): + del self.stack[prefix:] + + if prefix == len(self.stack): + self.stack.append(entry) + + # if the callstack has had an entry, it's this functions caller + if prefix > 0: + self.add_callee(self.stack[prefix - 1], entry) + + self.add_entry(entry) + + profile = Profile() + profile[SAMPLES] = 0 + for _function, _callees in self.entries.itervalues(): + function = Function(_function.id, _function.name) + function[SAMPLES] = _function.samples + profile.add_function(function) + profile[SAMPLES] += _function.samples + + if _function.image: + function[MODULE] = os.path.basename(_function.image) + + for _callee in _callees.itervalues(): + call = Call(_callee.id) + call[SAMPLES] = _callee.samples + function.add_call(call) + + # compute derived data + profile.validate() + profile.find_cycles() + profile.ratio(TIME_RATIO, SAMPLES) + profile.call_ratios(SAMPLES) + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) + + return profile + + +class SleepyParser(Parser): + """Parser for GNU gprof output. + + See also: + - http://www.codersnotes.com/sleepy/ + - http://sleepygraph.sourceforge.net/ + """ + + def __init__(self, filename): + Parser.__init__(self) + + from zipfile import ZipFile + + self.database = ZipFile(filename) + + self.symbols = {} + self.calls = {} + + self.profile = Profile() + + _symbol_re = re.compile( + r'^(?P\w+)' + + r'\s+"(?P[^"]*)"' + + r'\s+"(?P[^"]*)"' + + r'\s+"(?P[^"]*)"' + + r'\s+(?P\d+)$' + ) + + def parse_symbols(self): + lines = self.database.read('symbols.txt').splitlines() + for line in lines: + mo = self._symbol_re.match(line) + if mo: + symbol_id, module, procname, sourcefile, sourceline = mo.groups() + + function_id = ':'.join([module, procname]) + + try: + function = self.profile.functions[function_id] + except KeyError: + function = Function(function_id, procname) + function[SAMPLES] = 0 + self.profile.add_function(function) + + self.symbols[symbol_id] = function + + def parse_callstacks(self): + lines = self.database.read("callstacks.txt").splitlines() + for line in lines: + fields = line.split() + samples = int(fields[0]) + callstack = fields[1:] + + callstack = [self.symbols[symbol_id] for symbol_id in callstack] + + callee = callstack[0] + + callee[SAMPLES] += samples + self.profile[SAMPLES] += samples + + for caller in callstack[1:]: + try: + call = caller.calls[callee.id] + except KeyError: + call = Call(callee.id) + call[SAMPLES2] = samples + caller.add_call(call) + else: + call[SAMPLES2] += samples + + callee = caller + + def parse(self): + profile = self.profile + profile[SAMPLES] = 0 + + self.parse_symbols() + self.parse_callstacks() + + # Compute derived events + profile.validate() + profile.find_cycles() + profile.ratio(TIME_RATIO, SAMPLES) + profile.call_ratios(SAMPLES2) + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) + + return profile + + +class AQtimeTable: + + def __init__(self, name, fields): + self.name = name + + self.fields = fields + self.field_column = {} + for column in range(len(fields)): + self.field_column[fields[column]] = column + self.rows = [] + + def __len__(self): + return len(self.rows) + + def __iter__(self): + for values, children in self.rows: + fields = {} + for name, value in zip(self.fields, values): + fields[name] = value + children = dict([(child.name, child) for child in children]) + yield fields, children + raise StopIteration + + def add_row(self, values, children=()): + self.rows.append((values, children)) + + +class AQtimeParser(XmlParser): + + def __init__(self, stream): + XmlParser.__init__(self, stream) + self.tables = {} + + def parse(self): + self.element_start('AQtime_Results') + self.parse_headers() + results = self.parse_results() + self.element_end('AQtime_Results') + return self.build_profile(results) + + def parse_headers(self): + self.element_start('HEADERS') + while self.token.type == XML_ELEMENT_START: + self.parse_table_header() + self.element_end('HEADERS') + + def parse_table_header(self): + attrs = self.element_start('TABLE_HEADER') + name = attrs['NAME'] + id = int(attrs['ID']) + field_types = [] + field_names = [] + while self.token.type == XML_ELEMENT_START: + field_type, field_name = self.parse_table_field() + field_types.append(field_type) + field_names.append(field_name) + self.element_end('TABLE_HEADER') + self.tables[id] = name, field_types, field_names + + def parse_table_field(self): + attrs = self.element_start('TABLE_FIELD') + type = attrs['TYPE'] + name = self.character_data() + self.element_end('TABLE_FIELD') + return type, name + + def parse_results(self): + self.element_start('RESULTS') + table = self.parse_data() + self.element_end('RESULTS') + return table + + def parse_data(self): + rows = [] + attrs = self.element_start('DATA') + table_id = int(attrs['TABLE_ID']) + table_name, field_types, field_names = self.tables[table_id] + table = AQtimeTable(table_name, field_names) + while self.token.type == XML_ELEMENT_START: + row, children = self.parse_row(field_types) + table.add_row(row, children) + self.element_end('DATA') + return table + + def parse_row(self, field_types): + row = [None]*len(field_types) + children = [] + self.element_start('ROW') + while self.token.type == XML_ELEMENT_START: + if self.token.name_or_data == 'FIELD': + field_id, field_value = self.parse_field(field_types) + row[field_id] = field_value + elif self.token.name_or_data == 'CHILDREN': + children = self.parse_children() + else: + raise XmlTokenMismatch(" or ", self.token) + self.element_end('ROW') + return row, children + + def parse_field(self, field_types): + attrs = self.element_start('FIELD') + id = int(attrs['ID']) + type = field_types[id] + value = self.character_data() + if type == 'Integer': + value = int(value) + elif type == 'Float': + value = float(value) + elif type == 'Address': + value = int(value) + elif type == 'String': + pass + else: + assert False + self.element_end('FIELD') + return id, value + + def parse_children(self): + children = [] + self.element_start('CHILDREN') + while self.token.type == XML_ELEMENT_START: + table = self.parse_data() + assert table.name not in children + children.append(table) + self.element_end('CHILDREN') + return children + + def build_profile(self, results): + assert results.name == 'Routines' + profile = Profile() + profile[TIME] = 0.0 + for fields, tables in results: + function = self.build_function(fields) + children = tables['Children'] + for fields, _ in children: + call = self.build_call(fields) + function.add_call(call) + profile.add_function(function) + profile[TIME] = profile[TIME] + function[TIME] + profile[TOTAL_TIME] = profile[TIME] + profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) + return profile + + def build_function(self, fields): + function = Function(self.build_id(fields), self.build_name(fields)) + function[TIME] = fields['Time'] + function[TOTAL_TIME] = fields['Time with Children'] + #function[TIME_RATIO] = fields['% Time']/100.0 + #function[TOTAL_TIME_RATIO] = fields['% with Children']/100.0 + return function + + def build_call(self, fields): + call = Call(self.build_id(fields)) + call[TIME] = fields['Time'] + call[TOTAL_TIME] = fields['Time with Children'] + #call[TIME_RATIO] = fields['% Time']/100.0 + #call[TOTAL_TIME_RATIO] = fields['% with Children']/100.0 + return call + + def build_id(self, fields): + return ':'.join([fields['Module Name'], fields['Unit Name'], fields['Routine Name']]) + + def build_name(self, fields): + # TODO: use more fields + return fields['Routine Name'] + + +class PstatsParser: + """Parser python profiling statistics saved with te pstats module.""" + + def __init__(self, *filename): + import pstats + try: + self.stats = pstats.Stats(*filename) + except ValueError: + import hotshot.stats + self.stats = hotshot.stats.load(filename[0]) + self.profile = Profile() + self.function_ids = {} + + def get_function_name(self, (filename, line, name)): + module = os.path.splitext(filename)[0] + module = os.path.basename(module) + return "%s:%d:%s" % (module, line, name) + + def get_function(self, key): + try: + id = self.function_ids[key] + except KeyError: + id = len(self.function_ids) + name = self.get_function_name(key) + function = Function(id, name) + self.profile.functions[id] = function + self.function_ids[key] = id + else: + function = self.profile.functions[id] + return function + + def parse(self): + self.profile[TIME] = 0.0 + self.profile[TOTAL_TIME] = self.stats.total_tt + for fn, (cc, nc, tt, ct, callers) in self.stats.stats.iteritems(): + callee = self.get_function(fn) + callee[CALLS] = nc + callee[TOTAL_TIME] = ct + callee[TIME] = tt + self.profile[TIME] += tt + self.profile[TOTAL_TIME] = max(self.profile[TOTAL_TIME], ct) + for fn, value in callers.iteritems(): + caller = self.get_function(fn) + call = Call(callee.id) + if isinstance(value, tuple): + for i in xrange(0, len(value), 4): + nc, cc, tt, ct = value[i:i+4] + if CALLS in call: + call[CALLS] += cc + else: + call[CALLS] = cc + + if TOTAL_TIME in call: + call[TOTAL_TIME] += ct + else: + call[TOTAL_TIME] = ct + + else: + call[CALLS] = value + call[TOTAL_TIME] = ratio(value, nc)*ct + + caller.add_call(call) + #self.stats.print_stats() + #self.stats.print_callees() + + # Compute derived events + self.profile.validate() + self.profile.ratio(TIME_RATIO, TIME) + self.profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) + + return self.profile + + +class Theme: + + def __init__(self, + bgcolor = (0.0, 0.0, 1.0), + mincolor = (0.0, 0.0, 0.0), + maxcolor = (0.0, 0.0, 1.0), + fontname = "Arial", + minfontsize = 10.0, + maxfontsize = 10.0, + minpenwidth = 0.5, + maxpenwidth = 4.0, + gamma = 2.2, + skew = 1.0): + self.bgcolor = bgcolor + self.mincolor = mincolor + self.maxcolor = maxcolor + self.fontname = fontname + self.minfontsize = minfontsize + self.maxfontsize = maxfontsize + self.minpenwidth = minpenwidth + self.maxpenwidth = maxpenwidth + self.gamma = gamma + self.skew = skew + + def graph_bgcolor(self): + return self.hsl_to_rgb(*self.bgcolor) + + def graph_fontname(self): + return self.fontname + + def graph_fontsize(self): + return self.minfontsize + + def node_bgcolor(self, weight): + return self.color(weight) + + def node_fgcolor(self, weight): + return self.graph_bgcolor() + + def node_fontsize(self, weight): + return self.fontsize(weight) + + def edge_color(self, weight): + return self.color(weight) + + def edge_fontsize(self, weight): + return self.fontsize(weight) + + def edge_penwidth(self, weight): + return max(weight*self.maxpenwidth, self.minpenwidth) + + def edge_arrowsize(self, weight): + return 0.5 * math.sqrt(self.edge_penwidth(weight)) + + def fontsize(self, weight): + return max(weight**2 * self.maxfontsize, self.minfontsize) + + def color(self, weight): + weight = min(max(weight, 0.0), 1.0) + + hmin, smin, lmin = self.mincolor + hmax, smax, lmax = self.maxcolor + + if self.skew < 0: + raise ValueError("Skew must be greater than 0") + elif self.skew == 1.0: + h = hmin + weight*(hmax - hmin) + s = smin + weight*(smax - smin) + l = lmin + weight*(lmax - lmin) + else: + base = self.skew + h = hmin + ((hmax-hmin)*(-1.0 + (base ** weight)) / (base - 1.0)) + s = smin + ((smax-smin)*(-1.0 + (base ** weight)) / (base - 1.0)) + l = lmin + ((lmax-lmin)*(-1.0 + (base ** weight)) / (base - 1.0)) + + return self.hsl_to_rgb(h, s, l) + + def hsl_to_rgb(self, h, s, l): + """Convert a color from HSL color-model to RGB. + + See also: + - http://www.w3.org/TR/css3-color/#hsl-color + """ + + h = h % 1.0 + s = min(max(s, 0.0), 1.0) + l = min(max(l, 0.0), 1.0) + + if l <= 0.5: + m2 = l*(s + 1.0) + else: + m2 = l + s - l*s + m1 = l*2.0 - m2 + r = self._hue_to_rgb(m1, m2, h + 1.0/3.0) + g = self._hue_to_rgb(m1, m2, h) + b = self._hue_to_rgb(m1, m2, h - 1.0/3.0) + + # Apply gamma correction + r **= self.gamma + g **= self.gamma + b **= self.gamma + + return (r, g, b) + + def _hue_to_rgb(self, m1, m2, h): + if h < 0.0: + h += 1.0 + elif h > 1.0: + h -= 1.0 + if h*6 < 1.0: + return m1 + (m2 - m1)*h*6.0 + elif h*2 < 1.0: + return m2 + elif h*3 < 2.0: + return m1 + (m2 - m1)*(2.0/3.0 - h)*6.0 + else: + return m1 + + +TEMPERATURE_COLORMAP = Theme( + mincolor = (2.0/3.0, 0.80, 0.25), # dark blue + maxcolor = (0.0, 1.0, 0.5), # satured red + gamma = 1.0 +) + +PINK_COLORMAP = Theme( + mincolor = (0.0, 1.0, 0.90), # pink + maxcolor = (0.0, 1.0, 0.5), # satured red +) + +GRAY_COLORMAP = Theme( + mincolor = (0.0, 0.0, 0.85), # light gray + maxcolor = (0.0, 0.0, 0.0), # black +) + +BW_COLORMAP = Theme( + minfontsize = 8.0, + maxfontsize = 24.0, + mincolor = (0.0, 0.0, 0.0), # black + maxcolor = (0.0, 0.0, 0.0), # black + minpenwidth = 0.1, + maxpenwidth = 8.0, +) + + +class DotWriter: + """Writer for the DOT language. + + See also: + - "The DOT Language" specification + http://www.graphviz.org/doc/info/lang.html + """ + + def __init__(self, fp): + self.fp = fp + + def graph(self, profile, theme): + self.begin_graph() + + fontname = theme.graph_fontname() + + self.attr('graph', fontname=fontname, ranksep=0.25, nodesep=0.125) + self.attr('node', fontname=fontname, shape="box", style="filled", fontcolor="white", width=0, height=0) + self.attr('edge', fontname=fontname) + + for function in profile.functions.itervalues(): + labels = [] + for event in PROCESS, MODULE: + if event in function.events: + label = event.format(function[event]) + labels.append(label) + labels.append(function.name) + for event in TOTAL_TIME_RATIO, TIME_RATIO, CALLS: + if event in function.events: + label = event.format(function[event]) + labels.append(label) + + try: + weight = function[PRUNE_RATIO] + except UndefinedEvent: + weight = 0.0 + + label = '\n'.join(labels) + self.node(function.id, + label = label, + color = self.color(theme.node_bgcolor(weight)), + fontcolor = self.color(theme.node_fgcolor(weight)), + fontsize = "%.2f" % theme.node_fontsize(weight), + ) + + for call in function.calls.itervalues(): + callee = profile.functions[call.callee_id] + + labels = [] + for event in TOTAL_TIME_RATIO, CALLS: + if event in call.events: + label = event.format(call[event]) + labels.append(label) + + try: + weight = call[PRUNE_RATIO] + except UndefinedEvent: + try: + weight = callee[PRUNE_RATIO] + except UndefinedEvent: + weight = 0.0 + + label = '\n'.join(labels) + + self.edge(function.id, call.callee_id, + label = label, + color = self.color(theme.edge_color(weight)), + fontcolor = self.color(theme.edge_color(weight)), + fontsize = "%.2f" % theme.edge_fontsize(weight), + penwidth = "%.2f" % theme.edge_penwidth(weight), + labeldistance = "%.2f" % theme.edge_penwidth(weight), + arrowsize = "%.2f" % theme.edge_arrowsize(weight), + ) + + self.end_graph() + + def begin_graph(self): + self.write('digraph {\n') + + def end_graph(self): + self.write('}\n') + + def attr(self, what, **attrs): + self.write("\t") + self.write(what) + self.attr_list(attrs) + self.write(";\n") + + def node(self, node, **attrs): + self.write("\t") + self.id(node) + self.attr_list(attrs) + self.write(";\n") + + def edge(self, src, dst, **attrs): + self.write("\t") + self.id(src) + self.write(" -> ") + self.id(dst) + self.attr_list(attrs) + self.write(";\n") + + def attr_list(self, attrs): + if not attrs: + return + self.write(' [') + first = True + for name, value in attrs.iteritems(): + if first: + first = False + else: + self.write(", ") + self.id(name) + self.write('=') + self.id(value) + self.write(']') + + def id(self, id): + if isinstance(id, (int, float)): + s = str(id) + elif isinstance(id, basestring): + if id.isalnum(): + s = id + else: + s = self.escape(id) + else: + raise TypeError + self.write(s) + + def color(self, (r, g, b)): + + def float2int(f): + if f <= 0.0: + return 0 + if f >= 1.0: + return 255 + return int(255.0*f + 0.5) + + return "#" + "".join(["%02x" % float2int(c) for c in (r, g, b)]) + + def escape(self, s): + s = s.encode('utf-8') + s = s.replace('\\', r'\\') + s = s.replace('\n', r'\n') + s = s.replace('\t', r'\t') + s = s.replace('"', r'\"') + return '"' + s + '"' + + def write(self, s): + self.fp.write(s) + + +class Main: + """Main program.""" + + themes = { + "color": TEMPERATURE_COLORMAP, + "pink": PINK_COLORMAP, + "gray": GRAY_COLORMAP, + "bw": BW_COLORMAP, + } + + def main(self): + """Main program.""" + + parser = optparse.OptionParser( + usage="\n\t%prog [options] [file] ...", + version="%%prog %s" % __version__) + parser.add_option( + '-o', '--output', metavar='FILE', + type="string", dest="output", + help="output filename [stdout]") + parser.add_option( + '-n', '--node-thres', metavar='PERCENTAGE', + type="float", dest="node_thres", default=0.5, + help="eliminate nodes below this threshold [default: %default]") + parser.add_option( + '-e', '--edge-thres', metavar='PERCENTAGE', + type="float", dest="edge_thres", default=0.1, + help="eliminate edges below this threshold [default: %default]") + parser.add_option( + '-f', '--format', + type="choice", choices=('prof', 'oprofile', 'sysprof', 'pstats', 'shark', 'sleepy', 'aqtime'), + dest="format", default="prof", + help="profile format: prof, oprofile, sysprof, shark, sleepy, aqtime, or pstats [default: %default]") + parser.add_option( + '-c', '--colormap', + type="choice", choices=('color', 'pink', 'gray', 'bw'), + dest="theme", default="color", + help="color map: color, pink, gray, or bw [default: %default]") + parser.add_option( + '-s', '--strip', + action="store_true", + dest="strip", default=False, + help="strip function parameters, template parameters, and const modifiers from demangled C++ function names") + parser.add_option( + '-w', '--wrap', + action="store_true", + dest="wrap", default=False, + help="wrap function names") + # add a new option to control skew of the colorization curve + parser.add_option( + '--skew', + type="float", dest="theme_skew", default=1.0, + help="skew the colorization curve. Values < 1.0 give more variety to lower percentages. Value > 1.0 give less variety to lower percentages") + (self.options, self.args) = parser.parse_args(sys.argv[1:]) + + if len(self.args) > 1 and self.options.format != 'pstats': + parser.error('incorrect number of arguments') + + try: + self.theme = self.themes[self.options.theme] + except KeyError: + parser.error('invalid colormap \'%s\'' % self.options.theme) + + # set skew on the theme now that it has been picked. + if self.options.theme_skew: + self.theme.skew = self.options.theme_skew + + if self.options.format == 'prof': + if not self.args: + fp = sys.stdin + else: + fp = open(self.args[0], 'rt') + parser = GprofParser(fp) + elif self.options.format == 'oprofile': + if not self.args: + fp = sys.stdin + else: + fp = open(self.args[0], 'rt') + parser = OprofileParser(fp) + elif self.options.format == 'sysprof': + if not self.args: + fp = sys.stdin + else: + fp = open(self.args[0], 'rt') + parser = SysprofParser(fp) + elif self.options.format == 'pstats': + if not self.args: + parser.error('at least a file must be specified for pstats input') + parser = PstatsParser(*self.args) + elif self.options.format == 'shark': + if not self.args: + fp = sys.stdin + else: + fp = open(self.args[0], 'rt') + parser = SharkParser(fp) + elif self.options.format == 'sleepy': + if len(self.args) != 1: + parser.error('exactly one file must be specified for sleepy input') + parser = SleepyParser(self.args[0]) + elif self.options.format == 'aqtime': + if not self.args: + fp = sys.stdin + else: + fp = open(self.args[0], 'rt') + parser = AQtimeParser(fp) + else: + parser.error('invalid format \'%s\'' % self.options.format) + + self.profile = parser.parse() + + if self.options.output is None: + self.output = sys.stdout + else: + self.output = open(self.options.output, 'wt') + + self.write_graph() + + _parenthesis_re = re.compile(r'\([^()]*\)') + _angles_re = re.compile(r'<[^<>]*>') + _const_re = re.compile(r'\s+const$') + + def strip_function_name(self, name): + """Remove extraneous information from C++ demangled function names.""" + + # Strip function parameters from name by recursively removing paired parenthesis + while True: + name, n = self._parenthesis_re.subn('', name) + if not n: + break + + # Strip const qualifier + name = self._const_re.sub('', name) + + # Strip template parameters from name by recursively removing paired angles + while True: + name, n = self._angles_re.subn('', name) + if not n: + break + + return name + + def wrap_function_name(self, name): + """Split the function name on multiple lines.""" + + if len(name) > 32: + ratio = 2.0/3.0 + height = max(int(len(name)/(1.0 - ratio) + 0.5), 1) + width = max(len(name)/height, 32) + # TODO: break lines in symbols + name = textwrap.fill(name, width, break_long_words=False) + + # Take away spaces + name = name.replace(", ", ",") + name = name.replace("> >", ">>") + name = name.replace("> >", ">>") # catch consecutive + + return name + + def compress_function_name(self, name): + """Compress function name according to the user preferences.""" + + if self.options.strip: + name = self.strip_function_name(name) + + if self.options.wrap: + name = self.wrap_function_name(name) + + # TODO: merge functions with same resulting name + + return name + + def write_graph(self): + dot = DotWriter(self.output) + profile = self.profile + profile.prune(self.options.node_thres/100.0, self.options.edge_thres/100.0) + + for function in profile.functions.itervalues(): + function.name = self.compress_function_name(function.name) + + dot.graph(profile, self.theme) + + +if __name__ == '__main__': + Main().main() diff --git a/trunk/3rdparty/gprof/graphviz-2.36.0.zip b/trunk/3rdparty/gprof/graphviz-2.36.0.zip new file mode 100644 index 00000000..d4a09604 Binary files /dev/null and b/trunk/3rdparty/gprof/graphviz-2.36.0.zip differ diff --git a/trunk/3rdparty/gprof/readme.txt b/trunk/3rdparty/gprof/readme.txt new file mode 100644 index 00000000..3e104a9a --- /dev/null +++ b/trunk/3rdparty/gprof/readme.txt @@ -0,0 +1,36 @@ +gprof图形化输出工具: gprof2dot.py graphviz-2.18.tar.gz build_gprof2dot.sh + +dot: + http://www.graphviz.org/ + http://www.graphviz.org/Download_source.php + graphviz-2.18.tar.gz 绘图工具 + build_gprof2dot.sh 编译graphviz,命令为dot。 + 要求是sudoer,需要sudo make install。 + +gprof2dot.py: + 将gprof的日志绘图。 + +使用方法: +0. 若需要图形化,编译dot: + cd 3rdparty/gprof && bash build_gprof2dot.sh +1. srs配置时: + ./configure --with-gprof + 脚本会加入编译参数"-pg -lc_p",gcc -g -pg -lc_p -c xxx -o xxx.o,即在configure中打开 Performance="-pg -lc_p" + 链接时,加入链接选项"-pg",否则无法工作:gcc -pg -o srs xxxx.o,即在configure中打开 PerformanceLink="-pg" +2. 编译和启动程序:make && ./objs/srs -c conf/srs.conf + 退出程序,按CTRL+C,可以看到生成了gmon.out,这个就是性能的统计数据。 +3. gprof生成报表: + gprof -b ./objs/srs gmon.out > gprof.srs.log +4. 将报表生成图片: + ./3rdparty/gprof/gprof2dot.py gprof.srs.log | dot -Tpng -o ~/winlin.png + +缩写语句: + # 生成 ~/winlin.log ~/winlin.png + rm -f gmon.out; ./objs/srs -c conf/srs.conf + # 用户按CTRL+C + file="winlin";gprof -b ./objs/srs gmon.out > ~/${file}.log; ./3rdparty/gprof/gprof2dot.py ~/${file}.log | dot -Tpng -o ~/${file}.png + +备注: + 其实gprof生成的日志就可以看,不一定要图形化。 + 也就是dot和gprof2dot都不用执行。 + 参考:http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html diff --git a/trunk/3rdparty/gtest-1.6.0.zip b/trunk/3rdparty/gtest-1.6.0.zip new file mode 100644 index 00000000..f07c43a7 Binary files /dev/null and b/trunk/3rdparty/gtest-1.6.0.zip differ diff --git a/trunk/3rdparty/jquery-1.10.2.zip b/trunk/3rdparty/jquery-1.10.2.zip new file mode 100644 index 00000000..fdf2378f Binary files /dev/null and b/trunk/3rdparty/jquery-1.10.2.zip differ diff --git a/trunk/3rdparty/openssl-1.0.1f.zip b/trunk/3rdparty/openssl-1.0.1f.zip new file mode 100644 index 00000000..edcde9dc Binary files /dev/null and b/trunk/3rdparty/openssl-1.0.1f.zip differ diff --git a/trunk/3rdparty/patches/1.st.arm.patch b/trunk/3rdparty/patches/1.st.arm.patch new file mode 100644 index 00000000..1e182594 --- /dev/null +++ b/trunk/3rdparty/patches/1.st.arm.patch @@ -0,0 +1,21 @@ +Only in .: 1.st.arm.patch +diff -r -c ./md.h ../st-1.9-patch-arm/md.h +*** ./md.h 2009-10-02 02:46:43.000000000 +0800 +--- ../st-1.9-patch-arm/md.h 2014-03-16 20:49:03.845344804 +0800 +*************** +*** 422,428 **** + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 +! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[20] + #else + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ +--- 422,428 ---- + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 +! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] + #else + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ diff --git a/trunk/3rdparty/readme.txt b/trunk/3rdparty/readme.txt old mode 100755 new mode 100644 index be1a3f1c..315a7576 --- a/trunk/3rdparty/readme.txt +++ b/trunk/3rdparty/readme.txt @@ -6,6 +6,9 @@ nginx-1.5.7.zip st-1.9.zip basic framework for srs. + +openssl-1.0.1f.zip + openssl for SRS(with-ssl) RTMP complex handshake to delivery h264+aac stream. CherryPy-3.2.4.zip sample api server for srs. @@ -15,14 +18,31 @@ yasm-1.2.0.tar.gz lame-3.99.5.tar.gz libaacplus-2.0.2.tar.gz libaacplus-patch-26410-800.zip (26410-800.zip) +speex-1.2rc1.zip x264-snapshot-20131129-2245-stable.tar.bz2 (core.138) for srs to support live stream transcoding. remark: we use *.zip for all linux plantform. tools/ccache-3.1.9.zip to fast build. + +1.st.arm.Makefile.patch + stűûָcc + +gtest-1.6.0.zip + googleԪԿܡ + +gperftools-2.1.zip + googleܷͲԹߡ + ʹòοѹļеREADMEdocĿ¼ links: + nginx: + http://nginx.org/ + http-parser: + https://github.com/joyent/http-parser + state-threads: + http://sourceforge.net/projects/state-threads ffmpeg: http://ffmpeg.org/ http://ffmpeg.org/releases/ffmpeg-2.1.1.tar.gz @@ -44,4 +64,16 @@ links: cherrypy: http://www.cherrypy.org/ https://pypi.python.org/pypi/CherryPy/3.2.4 + openssl: + http://www.openssl.org/ + http://www.openssl.org/source/openssl-1.0.1f.tar.gz + gtest: + https://code.google.com/p/googletest + https://code.google.com/p/googletest/downloads/list + gperftools: + https://code.google.com/p/gperftools/ + https://code.google.com/p/gperftools/downloads/list + speex: + http://www.speex.org/downloads/ + http://downloads.xiph.org/releases/speex/speex-1.2rc1.tar.gz diff --git a/trunk/3rdparty/speex-1.2rc1.zip b/trunk/3rdparty/speex-1.2rc1.zip new file mode 100644 index 00000000..4743180c Binary files /dev/null and b/trunk/3rdparty/speex-1.2rc1.zip differ diff --git a/trunk/CMakeLists.txt b/trunk/CMakeLists.txt new file mode 100644 index 00000000..5d7ea46c --- /dev/null +++ b/trunk/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 2.6.4) +project(srs CXX) + +INCLUDE_DIRECTORIES(objs objs/st objs/hp objs/openssl src/core src/kernel src/rtmp src/app) + +set(SOURCE_FILES src/main/srs_main_server.cpp) +AUX_SOURCE_DIRECTORY(src/core SOURCE_FILES) +AUX_SOURCE_DIRECTORY(src/kernel SOURCE_FILES) +AUX_SOURCE_DIRECTORY(src/rtmp SOURCE_FILES) +AUX_SOURCE_DIRECTORY(src/app SOURCE_FILES) + +ADD_EXECUTABLE(srs ${SOURCE_FILES}) +TARGET_LINK_LIBRARIES(srs dl) +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/st/libst.a) +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libssl.a) +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libcrypto.a) +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/hp/libhttp_parser.a) + +IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a) + MESSAGE("srs_libs not found") + EXEC_PROGRAM(./configure) +ENDIF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a) + +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains") +MESSAGE(STATUS "use ./configure && make, @see https://github.com/winlinvip/simple-rtmp-server#usage") + diff --git a/trunk/auto/apps.sh b/trunk/auto/apps.sh index b2ac7afd..e6fd7d1a 100644 --- a/trunk/auto/apps.sh +++ b/trunk/auto/apps.sh @@ -1,16 +1,16 @@ -#!/bin/bash - +# generate the binary +# # params: -# $SRS_OBJS the objs directory. ie. objs -# $SRS_MAKEFILE the makefile name. ie. Makefile -# $MAIN_ENTRANCES array, all main entrance, disable all except the $APP_MAIN itself -# $MODULE_OBJS array, the objects to compile the app. -# $BUILD_KEY a string indicates the build key for Makefile. ie. dump -# $APP_MAIN the object file that contains main function. ie. your_app_main -# $APP_NAME the app name to output. ie. your_app -# $ModuleLibFiles array, the 3rdpart library file to link with. ie. (objs/st-1.9/obj/libst.a objs/libx264/obj/libx264.a) -# $LINK_OPTIONS the linker options. -# $SO_PATH the libssl.so.10 and other so file path. +# $SRS_OBJS the objs directory. ie. objs +# $SRS_MAKEFILE the makefile name. ie. Makefile +# +# $MAIN_ENTRANCES array, disable all except the $APP_MAIN itself. ie. ["srs_main_server" "srs_main_bandcheck"] +# $APP_MAIN the object file that contains main function. ie. srs_main_server +# $BUILD_KEY a string indicates the build key for Makefile. ie. srs +# $APP_NAME the app name to output. ie. srs +# $MODULE_OBJS array, the objects to compile the app. +# $ModuleLibFiles array, the 3rdpart library file to link with. ie. [objs/st-1.9/obj/libst.a objs/libx264/obj/libx264.a] +# $LINK_OPTIONS the linker options. ie. -ldl FILE=${SRS_OBJS}/${SRS_MAKEFILE} @@ -19,8 +19,11 @@ APP_TARGET="${SRS_OBJS}/${APP_NAME}" echo "generate app ${APP_NAME} depends..."; echo "# build ${APP_TARGET}" >> ${FILE} +# generate the binary depends, for example: +# srs: objs/srs echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE} - +# the link commands, for example: +# objs/srs: objs/src/core/srs_core.o echo -n "${APP_TARGET}: " >> ${FILE} for item in ${MODULE_OBJS[*]}; do FILE_NAME=`basename $item` @@ -50,7 +53,9 @@ echo "" >> ${FILE} echo "generate app ${APP_NAME} link..."; -echo -n " \$(LINK) ${PerformanceLink} -o ${APP_TARGET} " >> ${FILE} +# genereate the actual link command, for example: +# $(LINK) -o objs/srs objs/src/core/srs_core.o -ldl +echo -n " \$(LINK) -o ${APP_TARGET} " >> ${FILE} for item in ${MODULE_OBJS[*]}; do FILE_NAME=`basename $item` FILE_NAME=${FILE_NAME%.*} @@ -83,10 +88,4 @@ done echo -n "${LINK_OPTIONS}" >> ${FILE} echo "" >> ${FILE} -# set the so reference path. -if [[ ! -z ${SO_PATH} ]]; then - echo -n " @bash auto/set_so_rpath.sh ${SOPathTool} ${APP_TARGET} ${SO_PATH}" >> ${FILE} - echo "" >> ${FILE} -fi - echo -n "generate app ${APP_NAME} ok"; echo '!'; diff --git a/trunk/auto/build_ffmpeg.sh b/trunk/auto/build_ffmpeg.sh old mode 100644 new mode 100755 index 439f73ae..cc66eca4 --- a/trunk/auto/build_ffmpeg.sh +++ b/trunk/auto/build_ffmpeg.sh @@ -2,6 +2,11 @@ ff_src_dir="../../3rdparty" +# the jobs to make ffmpeg +if [[ "" -eq SRS_JOBS ]]; then + export SRS_JOBS="--jobs=1" +fi + ff_current_dir=$(pwd -P) ff_build_dir="${ff_current_dir}/_build" ff_release_dir="${ff_current_dir}/_release" @@ -9,6 +14,7 @@ echo "start to build the tools for transcode system:" echo "current_dir: ${ff_current_dir}" echo "build_dir: ${ff_build_dir}" echo "release_dir: ${ff_release_dir}" +echo "SRS_JOBS: ${SRS_JOBS}" mkdir -p ${ff_build_dir} mkdir -p ${ff_release_dir} @@ -27,7 +33,7 @@ else fi # add yasm to path, for x264 to use yasm directly. # ffmpeg can specifies the yasm path when configure it. -PATH=${PATH}:${ff_release_dir}/bin +export PATH=${PATH}:${ff_release_dir}/bin # libaacplus if [[ -f ${ff_release_dir}/lib/libaacplus.a ]]; then @@ -48,10 +54,21 @@ else echo "build lame-3.99.5" cd $ff_current_dir && rm -rf lame-3.99.5 && unzip -q ${ff_src_dir}/lame-3.99.5.zip && - cd lame-3.99.5 && ./configure --prefix=${ff_release_dir} --enable-static && make && make install + cd lame-3.99.5 && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build lame-3.99.5 failed"; exit 1; fi fi +# speex-1.2rc1 +if [[ -f ${ff_release_dir}/lib/libspeex.a ]]; then + echo "libspeex is ok" +else + echo "build speex-1.2rc1" + cd $ff_current_dir && + rm -rf speex-1.2rc1 && unzip -q ${ff_src_dir}/speex-1.2rc1.zip && + cd speex-1.2rc1 && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build speex-1.2rc1 failed"; exit 1; fi +fi + # x264 core.138 if [[ -f ${ff_release_dir}/lib/libx264.a ]]; then echo "x264 is ok" @@ -59,7 +76,11 @@ 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} --disable-opencl --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 --disable-avs --disable-swscale --disable-lavf \ + --disable-ffms --disable-gpac && + make ${SRS_JOBS} && make install ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build x264 failed"; exit 1; fi fi @@ -84,9 +105,9 @@ else --extra-ldflags='-L${ffmpeg_exported_release_dir}/lib -lm -ldl' \ --disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc \ --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 && - make && make install - ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build x264 failed"; exit 1; fi + --enable-libx264 --enable-libmp3lame --enable-libaacplus --enable-libspeex \ + --enable-pthreads --extra-libs=-lpthread \ + --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers && + make ${SRS_JOBS} && make install + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build ffmpeg failed"; exit 1; fi fi diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index a5247084..05ccc196 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -1,210 +1,425 @@ #!/bin/bash +# variables, parent script must set it: +# SRS_JOBS: the build jobs. +# SrsArmMakeOptions: the arm make options for ubuntu12(armhf, v7cpu) +# SRS_AUTO_HEADERS_H: the auto generated header file. + +##################################################################################### +##################################################################################### +# prepare the depends tools and libraries +# DEPENDS: options.sh, only when user options parsed, the depends tools are known. +##################################################################################### +##################################################################################### + +##################################################################################### +# utilities +##################################################################################### +function require_sudoer() +{ + sudo echo "" >/dev/null 2>&1 + + ret=$?; if [[ 0 -ne $ret ]]; then + echo "\"$1\" require sudoer failed. ret=$ret"; + exit $ret; + fi +} + # TODO: check gcc/g++ -echo "check gcc/g++/gdb/make/openssl-devel" +echo "check gcc/g++/gdb/make" echo "depends tools are ok" ##################################################################################### -# for Ubuntu +# for Ubuntu, auto install tools by apt-get ##################################################################################### +OS_IS_UBUNTU=NO function Ubuntu_prepare() { - uname -v|grep Ubuntu >/dev/null 2>&1 - ret=$?; if [[ 0 -ne $ret ]]; then - return; + if [ $SRS_CUBIE = YES ]; then + echo "for cubieboard, use ubuntu prepare" + else + uname -v|grep Ubuntu >/dev/null 2>&1 + ret=$?; if [[ 0 -ne $ret ]]; then + return 0; + fi fi + OS_IS_UBUNTU=YES 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 + require_sudoer "sudo apt-get install -y --force-yes gcc" + sudo apt-get install -y --force-yes gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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++ + require_sudoer "sudo apt-get install -y --force-yes g++" + sudo apt-get install -y --force-yes g++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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 + require_sudoer "sudo apt-get install -y --force-yes make" + sudo apt-get install -y --force-yes make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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" + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install patch" + require_sudoer "sudo apt-get install -y --force-yes patch" + sudo apt-get install -y --force-yes patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install patch 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" + if [ $SRS_FFMPEG_TOOL = YES ]; then + autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install autoconf" + require_sudoer "sudo apt-get install -y --force-yes autoconf" + sudo apt-get install -y --force-yes autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + 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 --force-yes libtool" + sudo apt-get install -y --force-yes libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install libtool success" + fi + + if [[ ! -f /usr/include/pcre.h ]]; then + echo "install libpcre3-dev" + require_sudoer "sudo apt-get install -y --force-yes libpcre3-dev" + sudo apt-get install -y --force-yes libpcre3-dev; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install libpcre3-dev success" + fi + + if [[ ! -f /usr/include/zlib.h ]]; then + echo "install zlib1g-dev" + require_sudoer "sudo apt-get install -y --force-yes zlib1g-dev" + sudo apt-get install -y --force-yes zlib1g-dev; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install zlib1g-dev success" + fi 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" + # for arm, install the cross build tool chain. + if [ $SRS_ARM_UBUNTU12 = YES ]; then + $SrsArmCC --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi" + require_sudoer "sudo apt-get install -y --force-yes gcc-arm-linux-gnueabi g++-arm-linux-gnueabi" + sudo apt-get install -y --force-yes gcc-arm-linux-gnueabi g++-arm-linux-gnueabi; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi success" + fi 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" + # for mips, user must installed the tool chain. + if [ $SRS_MIPS_UBUNTU12 = YES ]; then + $SrsArmCC --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "user must install the tool chain: $SrsArmCC" + return 2 + fi fi echo "Ubuntu install tools success" + return 0 } -Ubuntu_prepare +Ubuntu_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "Ubuntu prepare failed, ret=$ret"; exit $ret; fi ##################################################################################### -# for Centos +# for Centos, auto install tools by yum ##################################################################################### +OS_IS_CENTOS=NO function Centos_prepare() { if [[ ! -f /etc/redhat-release ]]; then - return; + return 0; fi + OS_IS_CENTOS=YES 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 + sudo yum install -y gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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++ + sudo yum install -y gcc-c++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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 + sudo yum install -y make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi 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" + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install patch" + require_sudoer "sudo yum install -y patch" + sudo yum install -y patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install patch 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" + if [ $SRS_FFMPEG_TOOL = YES ]; then + 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; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + 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; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + 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; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + 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; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + 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; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install zlib-devel success" + fi 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" + # for arm, install the cross build tool chain. + if [ $SRS_EMBEDED_CPU = YES ]; then + echo "embeded(arm/mips) is invalid for CentOS" + return 1 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" + echo "Centos install tools success" + return 0 +} +Centos_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "CentOS prepare failed, ret=$ret"; exit $ret; fi +##################################################################################### +# for OSX, auto install tools by brew +##################################################################################### +OS_IS_OSX=NO +function OSX_prepare() +{ + SYS_NAME=`uname -s` + if [ $SYS_NAME != Darwin ]; then + echo "This is not Darwin OSX" + return 0; + fi + + OS_IS_OSX=YES + echo "OSX detected, install tools if needed" + + gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install gcc" + require_sudoer "sudo brew install gcc" + sudo brew install gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install gcc success" + fi + + g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install gcc-c++" + require_sudoer "sudo brew install gcc-c++" + sudo brew install gcc-c++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install gcc-c++ 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" + make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install make" + require_sudoer "sudo brew install make" + sudo brew install make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install make 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" + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install patch" + require_sudoer "sudo brew install patch" + sudo brew install patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install patch 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" + if [ $SRS_FFMPEG_TOOL = YES ]; then + automake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install automake" + require_sudoer "sudo brew install automake" + sudo brew install automake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install automake success" + fi + + autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install autoconf" + require_sudoer "sudo brew install autoconf" + sudo brew install autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install autoconf success" + fi + + libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "install libtool" + require_sudoer "sudo brew install libtool" + sudo brew install libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install libtool success" + fi + + if [[ ! -f /usr/include/pcre.h ]]; then + echo "install pcre-devel" + require_sudoer "sudo brew install pcre-devel" + sudo brew install pcre-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install pcre-devel success" + fi + + if [[ ! -f /usr/include/zlib.h ]]; then + echo "install zlib-devel" + require_sudoer "sudo brew install zlib-devel" + sudo brew install zlib-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install zlib-devel success" + fi fi - echo "Centos install tools success" + echo "OSX install tools success" + return 0 } -Centos_prepare +OSX_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "OSX prepare failed, ret=$ret"; exit $ret; fi + ##################################################################################### # st-1.9 ##################################################################################### -if [[ -f ${SRS_OBJS}/st-1.9/obj/libst.a && -f ${SRS_OBJS}/st-1.9/obj/libst.so ]]; then - echo "st-1.9t is ok."; +# check the arm flag file, if flag changed, need to rebuild the st. +_ST_MAKE=linux-debug +if [ $SRS_EMBEDED_CPU = YES ]; then + # ok, arm specified, if the flag filed does not exists, need to rebuild. + if [[ -f ${SRS_OBJS}/_flag.st.arm.tmp && -f ${SRS_OBJS}/st/libst.a ]]; then + echo "st-1.9t for arm is ok."; + else + # TODO: FIXME: patch the bug. + # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm#st-arm-bug-fix + echo "build st-1.9t for arm"; + ( + rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && + unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 && + patch -p0 < ../../3rdparty/patches/1.st.arm.patch && + make CC=${SrsArmCC} AR=${SrsArmAR} LD=${SrsArmLD} RANDLIB=${SrsArmRANDLIB} EXTRA_CFLAGS="-DMD_HAVE_EPOLL" ${_ST_MAKE} && + cd .. && rm -rf st && ln -sf st-1.9/obj st && + cd .. && touch ${SRS_OBJS}/_flag.st.arm.tmp + ) + fi else - echo "build st-1.9t"; - ( - rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && - unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 && make linux-debug && - cd .. && rm -f st && ln -sf st-1.9/obj st - ) + if [ $SRS_OSX = YES ]; then + _ST_MAKE=darwin-debug + fi + if [[ ! -f ${SRS_OBJS}/_flag.st.arm.tmp && -f ${SRS_OBJS}/st/libst.a ]]; then + echo "st-1.9t is ok."; + else + echo "build st-1.9t"; + ( + rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && + unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 && + echo "we alaways patch the st, for we may build srs under arm directly" && + echo "the 1.st.arm.patch is ok for x86 because it's only modify code under macro linux arm" && + patch -p0 < ../../3rdparty/patches/1.st.arm.patch && + make ${_ST_MAKE} && + cd .. && rm -rf st && ln -sf st-1.9/obj st && + cd .. && rm -f ${SRS_OBJS}/_flag.st.arm.tmp + ) + fi fi # check status ret=$?; if [[ $ret -ne 0 ]]; then echo "build st-1.9 failed, ret=$ret"; exit $ret; fi -if [ ! -f ${SRS_OBJS}/st-1.9/obj/libst.a ]; then echo "build st-1.9 failed."; exit -1; fi -if [ ! -f ${SRS_OBJS}/st-1.9/obj/libst.so ]; then echo "build st-1.9 failed."; exit -1; fi +if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "build st-1.9 static lib failed."; exit -1; fi ##################################################################################### # http-parser-2.1 ##################################################################################### -if [ $SRS_HTTP = YES ]; then - if [[ -f ${SRS_OBJS}/http-parser-2.1/http_parser.h && -f ${SRS_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then - echo "http-parser-2.1 is ok."; +# check the arm flag file, if flag changed, need to rebuild the st. +if [ $SRS_HTTP_PARSER = YES ]; then + # for osx(darwin), donot use sed. + if [ $SRS_OSX = YES ]; then + if [[ -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then + echo "http-parser-2.1 is ok."; + else + echo "build http-parser-2.1 for osx(darwin)"; + ( + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip && + cd http-parser-2.1 && + make package && + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp + ) + fi + # ok, arm specified, if the flag filed does not exists, need to rebuild. + elif [ $SRS_EMBEDED_CPU = YES ]; then + if [[ -f ${SRS_OBJS}/_flag.st.hp.tmp && -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then + echo "http-parser-2.1 for arm is ok."; + else + echo "build http-parser-2.1 for arm"; + ( + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip && + cd http-parser-2.1 && + sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile && + sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile && + make CC=${SrsArmCC} AR=${SrsArmAR} package && + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp && + cd .. && touch ${SRS_OBJS}/_flag.st.hp.tmp + ) + fi else - echo "build http-parser-2.1"; - ( - rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip && - cd http-parser-2.1 && - sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile && - sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile && - make package && - cd .. && rm -f hp && ln -sf http-parser-2.1 hp - ) + # arm not specified, if exists flag, need to rebuild for no-arm platform. + if [[ ! -f ${SRS_OBJS}/_flag.st.hp.tmp && -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then + echo "http-parser-2.1 is ok."; + else + echo "build http-parser-2.1"; + ( + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip && + cd http-parser-2.1 && + sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile && + sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile && + make package && + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp && + cd .. && rm -f ${SRS_OBJS}/_flag.st.hp.tmp + ) + fi fi + # check status ret=$?; if [[ $ret -ne 0 ]]; then echo "build http-parser-2.1 failed, ret=$ret"; exit $ret; fi - if [[ ! -f ${SRS_OBJS}/http-parser-2.1/http_parser.h ]]; then echo "build http-parser-2.1 failed"; exit -1; fi - if [[ ! -f ${SRS_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then echo "build http-parser-2.1 failed"; exit -1; fi + if [[ ! -f ${SRS_OBJS}/hp/http_parser.h ]]; then echo "build http-parser-2.1 failed"; exit -1; fi + if [[ ! -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then echo "build http-parser-2.1 failed"; exit -1; fi +fi + +if [ $SRS_HTTP_PARSER = YES ]; then + echo "#define SRS_AUTO_HTTP_PARSER" >> $SRS_AUTO_HEADERS_H +else + echo "#undef SRS_AUTO_HTTP_PARSER" >> $SRS_AUTO_HEADERS_H +fi + +if [ $SRS_HTTP_SERVER = YES ]; then + echo "#define SRS_AUTO_HTTP_SERVER" >> $SRS_AUTO_HEADERS_H +else + echo "#undef SRS_AUTO_HTTP_SERVER" >> $SRS_AUTO_HEADERS_H +fi + +if [ $SRS_HTTP_API = YES ]; then + echo "#define SRS_AUTO_HTTP_API" >> $SRS_AUTO_HEADERS_H +else + echo "#undef SRS_AUTO_HTTP_API" >> $SRS_AUTO_HEADERS_H fi ##################################################################################### @@ -212,7 +427,7 @@ fi ##################################################################################### function write_nginx_html5() { - cat<> ${html_file} + cat< ${html_file}