winlin

Merge branch 'srs.1.0release' into 1.0release

要显示太多修改。

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

  1 +# Compiled Object files
  2 +*.slo
  3 +*.lo
  4 +*.o
  5 +
  6 +# Compiled Dynamic libraries
  7 +*.so
  8 +*.dylib
  9 +
  10 +# Compiled Static libraries
  11 +*.lai
  12 +*.la
  13 +*.a
  14 +
  15 +# by winlin
  16 +*.pyc
  17 +*.swp
  18 +/trunk/Makefile
  19 +/trunk/objs
  20 +/trunk/research/librtmp/objs
  21 +/trunk/3rdparty/ccache/ccache-3.1.9
  22 +/trunk/3rdparty/gprof/graphviz-2.36.0
  23 +/trunk/research/api-server/static-dir/crossdomain.xml
  24 +/trunk/research/api-server/static-dir/forward
  25 +/trunk/research/api-server/static-dir/live
  26 +/trunk/research/api-server/static-dir/players
  1 +Authors ordered by first contribution.
  2 +
  3 +* winlin<winlin@vip.126.com>
  4 +* wenjie.zhao<740936897@qq.com>
  5 +* xiangcheng.liu<liuxc0116@foxmail.com>
  6 +* naijia.liu<youngcow@youngcow.net>
  7 +* alcoholyi<alcoholyi@qq.com>
  8 +* byteman<wangchen2011@gmail.com>
  9 +* chad.wang<chad.wang.cn@gmail.com>
  10 +* suhetao<suhetao@gmail.com>
  11 +* Johnny<fengjihu@163.com>
  12 +* karthikeyan<keyanmca@gmail.com>
  13 +* StevenLiu<lq@chinaffmpeg.org>
  14 +* zhengfl<zhengfl_1989@126.com>
  1 +Donations ordered by first donation.
  2 +
  3 +* [2014-04-25 13:21] 刘连响 刘连响(492827340)
  4 +* [2014-04-25 13:25] 张瑞圣 大圣(5839109)
  5 +* [2014-04-25 13:31] 郭强 寒一冰(63395865)
  6 +* [2014-05-12 10:22] 陈晨 陈晨(undeadalpha@gmail.com)
  7 +* [2014-06-17 17:57] 陈江兵 将兵(176340267)
  8 +* [2014-07-24 08:52] 黄英才 贝奇小天狼星(303441547)
  9 +* [2014-07-30 11:29] 周凯 子陵(93632886)
  10 +* [2014-08-04 10:47] 宋志 胖胖(37210101)
  11 +* [2014-08-07 22:56] 陈亮 陈亮
  12 +* [2014-08-15 10:55] 雷健 万山奔一溪(76411408)
  13 +* [2014-08-15 13:31] ZACH ZACH(18601653557)
  14 +* [2014-08-19 20:00] Matthew Matthew(1206651693)
  15 +* [2014-08-20 20:13] 林瑞潮 甲子(459505921)
  16 +* [2014-09-05 16:13] 于冰 秋雨☆ice(3373749)
  17 +
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) 2013-2014 winlin
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  6 +this software and associated documentation files (the "Software"), to deal in
  7 +the Software without restriction, including without limitation the rights to
  8 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  9 +the Software, and to permit persons to whom the Software is furnished to do so,
  10 +subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  17 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  18 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1 +#Simple-RTMP-Server
  2 +
  3 +SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。
  4 +
  5 +下载发布版(github):
  6 +[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip)
  7 +其他[more...](http://winlinvip.github.io/srs.release/releases/) <br/>
  8 +下载发布版(国内阿里云镜像):
  9 +[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip)
  10 +其他[more...](http://www.ossrs.net/srs.release/releases/)<br/>
  11 +同类产品:[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)
  12 +
  13 +获得源码(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)
  14 +
  15 +```bash
  16 +git clone https://github.com/winlinvip/simple-rtmp-server.git
  17 +```
  18 +
  19 +获得源码(国内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)
  20 +
  21 +```bash
  22 +git clone https://code.csdn.net/winlinvip/srs-csdn.git
  23 +```
  24 +
  25 +报告问题(BugReport): [https://github.com/winlinvip/simple-rtmp-server/issues/new](https://github.com/winlinvip/simple-rtmp-server/issues/new)<br/>
  26 +中文资料(Wiki): [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki) <br/>
  27 +使用步骤(Usage): [https://github.com/winlinvip/simple-rtmp-server#usage](#usage) <br/>
  28 +公用机器(LiveShow): [https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LiveShow](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LiveShow) <br/>
  29 +捐款(Donation): [GitHub](http://winlinvip.github.io/srs.release/donation/index.html)
  30 +[阿里云镜像](http://www.ossrs.net/srs.release/donation/index.html) ,查看
  31 +[捐献墙(Donations)](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt)<br/>
  32 +
  33 +## About
  34 +
  35 +SRS(SIMPLE RTMP Server) over state-threads created in 2013.10.
  36 +
  37 +SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP)/[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS),
  38 +[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), single/multiple(plan) processes, edge/origin live server,
  39 +[x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm),
  40 +compile depends on [st](http://sourceforge.net/projects/state-threads)(required), [ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser),
  41 +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
  42 +minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build).
  43 +
  44 +SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost),
  45 +rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) pull), [ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest),
  46 +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), [HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly), [transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG),
  47 +[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi),
  48 +[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer), [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR).
  49 +
  50 +Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release) <br/>
  51 +Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/>
  52 +CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) <br/>
  53 +See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) <br/>
  54 +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) <br/>
  55 +Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki) <br/>
  56 +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)
  57 +
  58 +## AUTHORS
  59 +The PRIMARY AUTHORS are (and/or have been)(Authors ordered by first contribution):
  60 +* winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
  61 +* wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html)
  62 +
  63 +About the primary AUTHORS:
  64 +* Contribute important features to SRS.
  65 +* Names of all PRIMARY AUTHORS response in NetConnection.connect and metadata.
  66 +* Names of all CONTRIBUTORS response in api/v1/authors.
  67 +
  68 +And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
  69 +people who have submitted patches, reported bugs, added translations, helped
  70 +answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt)
  71 +
  72 +A big THANK YOU goes to:
  73 +* [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).
  74 +* Genes amd Mabbott for creating [st](https://github.com/winlinvip/state-threads)([state-threads](http://sourceforge.net/projects/state-threads/)).
  75 +* Michael Talyanksy for introducing us to use st.
  76 +* Roman Arutyunyan for creating [nginx-rtmp](https://github.com/arut/nginx-rtmp-module) for SRS to refer to.
  77 +* Joyent for creating [http-parser](https://github.com/joyent/http-parser) for http-api for SRS.
  78 +* Igor Sysoev for creating [nginx](http://nginx.org/) for SRS to refer to.
  79 +* [FFMPEG](http://ffmpeg.org/) and [libx264](http://www.videolan.org/) group for SRS to use to transcode.
  80 +* Guido van Rossum for creating Python for api-server for SRS.
  81 +
  82 +## Usage
  83 +
  84 +<strong>Step 1:</strong> get SRS
  85 +
  86 +<pre>
  87 +git clone https://github.com/winlinvip/simple-rtmp-server &&
  88 +cd simple-rtmp-server/trunk
  89 +</pre>
  90 +
  91 +<strong>Step 2:</strong> build SRS,
  92 +<strong>Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build)</strong>
  93 +
  94 +<pre>
  95 +./configure && make
  96 +</pre>
  97 +
  98 +<strong>Step 3:</strong> start SRS
  99 +
  100 +<pre>
  101 +./objs/srs -c conf/srs.conf
  102 +</pre>
  103 +
  104 +<strong>See also:</strong>
  105 +* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP)
  106 +* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS)
  107 +* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS)
  108 +* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG)
  109 +* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward)
  110 +* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime)
  111 +* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleARM)
  112 +* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest)
  113 +* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP)
  114 +* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo)
  115 +* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Sample)
  116 +* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product)
  117 +
  118 +## Wiki
  119 +
  120 +Please select your language:
  121 +* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home)
  122 +* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home)
  123 +
  124 +## Donation
  125 +
  126 +[http://winlinvip.github.io/srs.release/donation/index.html](http://winlinvip.github.io/srs.release/donation/index.html)
  127 +OR [aliyun mirror](http://www.ossrs.net/srs.release/donation/index.html)
  128 +
  129 +Donations:<br/>
  130 +[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt)
  131 +
  132 +## System Requirements
  133 +Supported operating systems and hardware:
  134 +* All Linux , both 32 and 64 bits
  135 +* All hardware.
  136 +
  137 +## Summary
  138 +1. 简洁稳定:Simple, also stable enough.
  139 +1. 高性能:[High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance): single-thread, async socket, event/st-thread driven.
  140 +1. 高并发:[High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
  141 +1. RTMP源站:Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP).
  142 +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
  143 +1. 单进程(无多进程):Support single process; no multiple processes.
  144 +1. 支持Vhost:Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), support \_\_defaultVhost\_\_.
  145 +1. 直播(无点播):Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP) live streaming; no vod streaming.
  146 +1. 苹果HLS:Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) live streaming.
  147 +1. 支持纯音频HLS:Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly) live streaming.
  148 +1. 支持Reload:Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config to enable changes.
  149 +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.
  150 +1. 侦听多端口:Support listen at multiple ports.
  151 +1. 长时间推流:Support long time(>4.6hours) publish/play.
  152 +1. 转发流:Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) in master-slave mode.
  153 +1. 流转码:Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) by ffmpeg.
  154 +1. 支持FFMPEG滤镜:Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an.
  155 +1. 只转码音频:Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) only, speex/mp3 to aac
  156 +1. 支持HTTP回调:Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback)(for authentication and injection).
  157 +1. 带宽测速:Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_BandwidthTestTool) api and flash client.
  158 +1. 演示页面:Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo).
  159 +1. 视频会议演示:[Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap).
  160 +1. 中文Wiki:Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home), in Chineses.
  161 +1. 客户端库:Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp)
  162 +1. 支持ARM平台:Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp.
  163 +1. 支持Init.d脚本:Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService) and packge script, log to file.
  164 +1. 支持ATC:Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) for HLS/HDS to support backup(failover)
  165 +1. 支持HTTP-RESTful-API:Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi).
  166 +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).
  167 +1. 支持录制:Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), record live to flv file for vod.
  168 +1. 可追溯日志:Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog).
  169 +1. 支持FMS-Token穿越:Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse) for fms origin authenticate.
  170 +1. 全面的Utest:Support system full utest on gtest.
  171 +1. (不稳定)内嵌HTTP服务器:[experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP) for hls(live/vod)
  172 +1. (不稳定)FLV点播流:[experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FlvVodStream).
  173 +1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
  174 +1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).
  175 +1. [no-plan] Support multiple processes, for both origin and edge
  176 +1. [no-plan] Support adobe RTMFP(flash p2p) protocol.
  177 +1. [no-plan] Support adobe flash refer/token/swf verification.
  178 +1. [no-plan] Support adobe amf3 codec.
  179 +1. [no-plan] Support encryption: RTMPE/RTMPS, HLS DRM
  180 +1. [no-plan] Support RTMPT, http to tranverse firewalls
  181 +1. [no-plan] Support file source, transcoding file to live stream
  182 +1. [no-plan] Support RTP/RTSP server.
  183 +
  184 +## Releases
  185 +* 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.<br/>
  186 +* 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.<br/>
  187 +* 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.<br/>
  188 +* 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.<br/>
  189 +* 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.<br/>
  190 +* 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.<br/>
  191 +* 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.<br/>
  192 +* 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.<br/>
  193 +* 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.<br/>
  194 +* 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.<br/>
  195 +* 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.<br/>
  196 +* 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.<br/>
  197 +* 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.<br/>
  198 +* 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.<br/>
  199 +* 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.<br/>
  200 +* 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.<br/>
  201 +* 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.<br/>
  202 +* 2013-10-17, Created.<br/>
  203 +
  204 +## History
  205 +* v1.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 1.0.3.
  206 +* v1.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 1.0.2.
  207 +* v1.0, 2014-10-19, fix [#183](https://github.com/winlinvip/simple-rtmp-server/issues/183), hotfix for bug #183, donot support AnnexB when decoding RTMP body for HLS. 1.0.1.
  208 +* <strong>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.</strong>
  209 +* 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.
  210 +* v1.0, 2014-10-08, fix [#162](https://github.com/winlinvip/simple-rtmp-server/issues/162), failed if no epoll. 0.9.222.
  211 +* 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.
  212 +* v1.0, 2014-09-26, fix hls bug, refine config and log, according to clion of jetbrains. 0.9.216.
  213 +* 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.
  214 +* v1.0, 2014-08-28, fix [#167](https://github.com/winlinvip/simple-rtmp-server/issues/167), add openssl includes to utest. 0.9.209.
  215 +* v1.0, 2014-08-27, max connections is 32756, for st use mmap default. 0.9.209
  216 +* 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.
  217 +* 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.
  218 +* 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.
  219 +* 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.
  220 +* 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.
  221 +* v1.0, 2014-08-06, fix [#147](https://github.com/winlinvip/simple-rtmp-server/issues/147), support identify the srs edge. 0.9.190.
  222 +* <strong>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.</strong>
  223 +* 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.
  224 +* 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.
  225 +* 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.
  226 +* 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.
  227 +* 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.
  228 +* 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.
  229 +* 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.
  230 +* 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.
  231 +* v1.0, 2014-07-27, support partially build on OSX(Darwin). 0.9.177
  232 +* v1.0, 2014-07-27, api connections add udp, add disk iops. 0.9.176
  233 +* v1.0, 2014-07-26, complete config utest. 0.9.173
  234 +* 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.
  235 +* 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.
  236 +* 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.
  237 +* <strong>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.</strong>
  238 +* v1.0, 2014-07-13, refine the bandwidth check/test, add as/js library, use srs-librtmp for linux tool. 0.9.159
  239 +* v1.0, 2014-07-12, complete rtmp stack utest. 0.9.156
  240 +* 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.
  241 +* v1.0, 2014-07-06, fix [#103](https://github.com/winlinvip/simple-rtmp-server/issues/103), support all aac sample rate. 0.9.150.
  242 +* v1.0, 2014-07-05, complete kernel utest. 0.9.149
  243 +* v1.0, 2014-06-30, fix [#111](https://github.com/winlinvip/simple-rtmp-server/issues/111), always use 31bits timestamp. 0.9.143.
  244 +* v1.0, 2014-06-28, response the call message with null. 0.9.137
  245 +* 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
  246 +* 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
  247 +* <strong>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.</strong>
  248 +* v1.0, 2014-06-27, SRS online 30days with RTMP/HLS.
  249 +* 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
  250 +* v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132
  251 +* 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
  252 +* 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
  253 +* v1.0, 2014-06-19, add connections count to api summaries. 0.9.127
  254 +* v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126
  255 +* v1.0, 2014-06-18, add network bytes to api summaries. 0.9.125
  256 +* 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
  257 +* v1.0, 2014-05-29, support flv inject and flv http streaming with start=bytes. 0.9.122
  258 +* <strong>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.</strong>
  259 +* 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
  260 +* v1.0, 2014-05-27, fix [#84](https://github.com/winlinvip/simple-rtmp-server/issues/84), unpublish when edge disconnect. 0.9.119
  261 +* 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
  262 +* 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
  263 +* v1.0, 2014-05-24, Johnny contribute [ossrs.net](http://ossrs.net). karthikeyan start to translate wiki to English.
  264 +* 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
  265 +* v1.0, 2014-05-22, support amf0 StrictArray(0x0a). 0.9.111.
  266 +* v1.0, 2014-05-22, support flv parser, add amf0 to librtmp. 0.9.110
  267 +* 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
  268 +* v1.0, 2014-05-19, support http heartbeat, 0.9.107
  269 +* <strong>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.</strong>
  270 +* v1.0, 2014-05-18, support http api json, to PUT/POST. 0.9.105
  271 +* 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
  272 +* v1.0, 2014-05-17, rename struct to class. 0.9.100
  273 +* 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
  274 +* v1.0, 2014-05-13, fix mem leak for delete[] SharedPtrMessage array. 0.9.95
  275 +* v1.0, 2014-05-12, refine the kbps calc module. 0.9.93
  276 +* v1.0, 2014-05-12, fix bug [#64](https://github.com/winlinvip/simple-rtmp-server/issues/64): install_dir=DESTDIR+PREFIX
  277 +* v1.0, 2014-05-08, fix [#36](https://github.com/winlinvip/simple-rtmp-server/issues/36): never directly use \*(int32_t\*) for arm.
  278 +* v1.0, 2014-05-08, fix [#60](https://github.com/winlinvip/simple-rtmp-server/issues/60): support aggregate message
  279 +* v1.0, 2014-05-08, fix [#59](https://github.com/winlinvip/simple-rtmp-server/issues/59), edge support FMS origin server. 0.9.92
  280 +* v1.0, 2014-05-06, fix [#50](https://github.com/winlinvip/simple-rtmp-server/issues/50), ubuntu14 build error.
  281 +* v1.0, 2014-05-04, support mips linux.
  282 +* 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
  283 +* v1.0, 2014-04-29, refine RTMP protocol completed, to 0.9.81
  284 +* <strong>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.</strong>
  285 +* v1.0, 2014-04-28, support full edge RTMP server. 0.9.79
  286 +* v1.0, 2014-04-27, support basic edge(play/publish) RTMP server. 0.9.78
  287 +* v1.0, 2014-04-25, add donation page. 0.9.76
  288 +* v1.0, 2014-04-21, support android app to start srs for internal edge. 0.9.72
  289 +* v1.0, 2014-04-19, support tool over srs-librtmp to ingest flv/rtmp. 0.9.71
  290 +* v1.0, 2014-04-17, support dvr(record live to flv file for vod). 0.9.69
  291 +* v1.0, 2014-04-11, add speex1.2 to transcode flash encoder stream. 0.9.58
  292 +* v1.0, 2014-04-10, support reload ingesters(add/remov/update). 0.9.57
  293 +* <strong>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.</strong>
  294 +* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest) file/stream/device.
  295 +* 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).
  296 +* v1.0, 2014-04-03, implements http framework and api/v1/version.
  297 +* v1.0, 2014-03-30, fix bug for st detecting epoll failed, force st to use epoll.
  298 +* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi).
  299 +* v1.0, 2014-03-29, add release binary package for raspberry-pi.
  300 +* v1.0, 2014-03-26, support RTMP ATC for HLS/HDS to support backup(failover).
  301 +* v1.0, 2014-03-23, support daemon, default start in daemon.
  302 +* v1.0, 2014-03-22, support make install/install-api and uninstall.
  303 +* v1.0, 2014-03-22, add ./etc/init.d/srs, refine to support make clean then make.
  304 +* v1.0, 2014-03-21, write pid to ./objs/srs.pid.
  305 +* v1.0, 2014-03-20, refine hls code, support pure audio HLS.
  306 +* v1.0, 2014-03-19, add vn/an for FFMPEG to drop video/audio for radio stream.
  307 +* v1.0, 2014-03-19, refine handshake, client support complex handshake, add utest.
  308 +* 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)
  309 +* 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.
  310 +* v1.0, 2014-03-12, finish utest for amf0 codec.
  311 +* v1.0, 2014-03-06, add gperftools for mem leak detect, mem/cpu profile.
  312 +* v1.0, 2014-03-04, add gest framework for utest, build success.
  313 +* 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)
  314 +* v1.0, 2014-03-02, srs-librtmp, client publish/play library like librtmp.
  315 +* v1.0, 2014-03-01, modularity, extract core/kernel/rtmp/app/main module.
  316 +* v1.0, 2014-02-28, support arm build(SRS/ST), add ssl to 3rdparty package.
  317 +* 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)
  318 +* 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)
  319 +* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS)
  320 +* 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)
  321 +* v1.0, 2014-01-11, fix jw/flower player pause bug, which send closeStream actually.
  322 +* 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)
  323 +* v1.0, 2014-01-01, change listen(512), chunk-size(60000), to improve performance.
  324 +* v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature.
  325 +* <strong>v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines.</strong>
  326 +* v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone.
  327 +* v0.9, 2013-12-22, demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
  328 +* v0.9, 2013-12-22, merge from wenjie, support banwidth test.
  329 +* v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level
  330 +* v0.9, 2013-12-21, add [players](http://demo.srs.com/players) for play and publish.
  331 +* v0.9, 2013-12-15, ensure the HLS(ts) is continous when republish stream.
  332 +* v0.9, 2013-12-15, fix the hls reload bug, feed it the sequence header.
  333 +* v0.9, 2013-12-15, refine protocol, use int64_t timestamp for ts and jitter.
  334 +* v0.9, 2013-12-15, support set the live queue length(in seconds), drop when full.
  335 +* v0.9, 2013-12-15, fix the forwarder reconnect bug, feed it the sequence header.
  336 +* v0.9, 2013-12-15, support reload the hls/forwarder/transcoder.
  337 +* v0.9, 2013-12-14, refine the thread model for the retry threads.
  338 +* v0.9, 2013-12-10, auto install depends tools/libs on centos/ubuntu.
  339 +* <strong>v0.8, 2013-12-08, [v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8) released. 19186 lines.</strong>
  340 +* 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.
  341 +* v0.8, 2013-12-08, support multiple http hooks for a event.
  342 +* v0.8, 2013-12-07, support http callback hooks, on_connect.
  343 +* v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4.
  344 +* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use SRS rtmp sdk.
  345 +* v0.8, 2013-12-06, support max_connections, drop if exceed.
  346 +* v0.8, 2013-12-05, support log_dir, write ffmpeg log to file.
  347 +* v0.8, 2013-12-05, fix the forward/hls/encoder bug.
  348 +* <strong>v0.7, 2013-12-03, [v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7) released. 17605 lines.</strong>
  349 +* v0.7, 2013-12-01, support dead-loop detect for forwarder and transcoder.
  350 +* v0.7, 2013-12-01, support all ffmpeg filters and params.
  351 +* v0.7, 2013-11-30, support live stream transcoder by ffmpeg.
  352 +* v0.7, 2013-11-30, support --with/without -ffmpeg, build ffmpeg-2.1.
  353 +* v0.7, 2013-11-30, add ffmpeg-2.1, x264-core138, lame-3.99.5, libaacplus-2.0.2.
  354 +* <strong>v0.6, 2013-11-29, [v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6) released. 16094 lines.</strong>
  355 +* v0.6, 2013-11-29, add performance summary, 1800 clients, 900Mbps, CPU 90.2%, 41MB.
  356 +* v0.6, 2013-11-29, support forward stream to other edge server.
  357 +* v0.6, 2013-11-29, support forward stream to other origin server.
  358 +* v0.6, 2013-11-28, fix memory leak bug, aac decode bug.
  359 +* v0.6, 2013-11-27, support --with or --without -hls and -ssl options.
  360 +* v0.6, 2013-11-27, support AAC 44100HZ sample rate for iphone, adjust the timestamp.
  361 +* <strong>v0.5, 2013-11-26, [v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5) released. 14449 lines.</strong>
  362 +* v0.5, 2013-11-24, support HLS(m3u8), fragment and window.
  363 +* v0.5, 2013-11-24, support record to ts file for HLS.
  364 +* v0.5, 2013-11-21, add ts_info tool to demux ts file.
  365 +* v0.5, 2013-11-16, add rtmp players(OSMF/jwplayer5/jwplayer6).
  366 +* <strong>v0.4, 2013-11-10, [v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4) released. 12500 lines.</strong>
  367 +* v0.4, 2013-11-10, support config and reload the pithy print.
  368 +* v0.4, 2013-11-09, support reload config(vhost and its detail).
  369 +* v0.4, 2013-11-09, support reload config(listen and chunk_size) by SIGHUP(1).
  370 +* v0.4, 2013-11-09, support longtime(>4.6hours) publish/play.
  371 +* v0.4, 2013-11-09, support config the chunk_size.
  372 +* v0.4, 2013-11-09, support pause for live stream.
  373 +* <strong>v0.3, 2013-11-04, [v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3) released. 11773 lines.</strong>
  374 +* v0.3, 2013-11-04, support refer/play-refer/publish-refer.
  375 +* v0.3, 2013-11-04, support vhosts specified config.
  376 +* v0.3, 2013-11-02, support listen multiple ports.
  377 +* v0.3, 2013-11-02, support config file in nginx-conf style.
  378 +* v0.3, 2013-10-29, support pithy print log message specified by stage.
  379 +* v0.3, 2013-10-28, support librtmp without extended-timestamp in 0xCX chunk packet.
  380 +* v0.3, 2013-10-27, support cache last gop for client fast startup.
  381 +* <strong>v0.2, 2013-10-25, [v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2) released. 10125 lines.</strong>
  382 +* v0.2, 2013-10-25, support flash publish.
  383 +* v0.2, 2013-10-25, support h264/avc codec by rtmp complex handshake.
  384 +* v0.2, 2013-10-24, support time jitter detect and correct algorithm
  385 +* v0.2, 2013-10-24, support decode codec type to cache the h264/avc sequence header.
  386 +* <strong>v0.1, 2013-10-23, [v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1) released. 8287 lines.</strong>
  387 +* v0.1, 2013-10-23, support basic amf0 codec, simplify the api using c-style api.
  388 +* v0.1, 2013-10-23, support shared ptr msg for zero memory copy.
  389 +* v0.1, 2013-10-22, support vp6 codec with rtmp protocol specified simple handshake.
  390 +* v0.1, 2013-10-20, support multiple flash client play live streaming.
  391 +* v0.1, 2013-10-20, support FMLE/FFMPEG publish live streaming.
  392 +* v0.1, 2013-10-18, support rtmp message2chunk protocol(send\_message).
  393 +* v0.1, 2013-10-17, support rtmp chunk2message protocol(recv\_message).
  394 +
  395 +## Performance
  396 +
  397 +Performance benchmark history, on virtual box:
  398 +
  399 +* 2014-07-12, SRS 0.9.156, 2700clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9)
  400 +* 2014-07-12, SRS 0.9.156, 1800clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a)
  401 +* 2013-11-28, SRS 0.5.0, 1800clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679)
  402 +
  403 +Latest benchmark:
  404 +
  405 +1. 300 connections, 150Mbps, 500kbps, CPU 5.7%, MEM 9208KB.
  406 +1. 600 connections, 300Mbps, 500kbps, CPU 18.3%, MEM 13MB.
  407 +1. 900 connections, 450Mbps, 500kbps, CPU 27.9%, MEM 20MB.
  408 +1. 1200 connections, 600Mbps, 500kbps, CPU 43.9%, MEM 26MB.
  409 +1. 1500 connections, 750Mbps, 500kbps, CPU 55.2%, MEM 32MB.
  410 +1. 1800 connections, 900Mbps, 500kbps, CPU 68.8%, MEM 38MB.
  411 +1. 2100 connections, 1050Mbps, 500kbps, CPU 75.7%, MEM 46MB.
  412 +1. 2400 connections, 1200Mbps, 500kbps, CPU 83.7%, MEM 54MB.
  413 +1. 2700 connections, 1350Mbps, 500kbps, CPU 89.9%, MEM 61MB.
  414 +
  415 +<pre>
  416 +[winlin@dev6 srs]$ dstat
  417 +----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
  418 +usr sys idl wai hiq siq| read writ| recv send| in out | int csw
  419 + 29 17 39 0 0 15| 0 5325B| 163M 163M| 0 0 |4331 3386
  420 + 30 16 38 0 0 16| 0 5325B| 160M 160M| 0 0 |4252 3332
  421 + 30 15 37 0 0 17| 0 7646B| 169M 169M| 0 0 |4015 2886
  422 + 30 17 36 0 0 17| 0 1638B| 197M 197M| 0 0 |4021 3037
  423 + 31 17 35 0 0 17| 0 410B| 204M 204M| 0 0 |4181 3243
  424 + 33 17 32 0 0 18| 0 2185B| 191M 191M| 0 0 |4305 3592
  425 + 31 15 36 0 0 18| 0 1229B| 127M 127M| 0 0 |4446 3822
  426 + 34 18 30 0 0 18| 0 0 | 231M 231M| 0 0 |4461 3691
  427 + 32 17 33 0 0 18| 0 410B| 169M 169M| 0 0 |4518 3788
  428 +</pre>
  429 +
  430 +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance)
  431 +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi)
  432 +
  433 +## Architecture
  434 +
  435 +SRS always use the most simple architecture to support complex transaction.
  436 +* System arch: the system structure and arch.
  437 +* Modularity arch: the main modularity of SRS.
  438 +* Stream arch: the stream dispatch arch of SRS.
  439 +* RTMP cluster arch: the RTMP origin and edge cluster arch.
  440 +* Multiple processes arch (by wenjie): the multiple process of SRS.
  441 +* CLI arch: the cli arch for SRS, api to manage SRS.
  442 +* Bandwidth specification: the bandwidth test specification of SRS.
  443 +
  444 +### System Architecture
  445 +
  446 +<pre>
  447 ++------------------------------------------------------+
  448 +| SRS(Simple RTMP Server) |
  449 ++---------------+---------------+-----------+----------+
  450 +| API/hook | Transcoder | HLS | RTMP |
  451 +| http-parser | FFMPEG/x264 | NGINX/ts | protocol |
  452 ++---------------+---------------+-----------+----------+
  453 +| Network(state-threads) |
  454 ++------------------------------------------------------+
  455 +| All Linux(RHEL,CentOS,Ubuntu,Fedora...) |
  456 ++------------------------------------------------------+
  457 +</pre>
  458 +
  459 +### Modularity Architecture
  460 +
  461 +<pre>
  462 ++------------------------------------------------------+
  463 +| Main(srs/bandwidth/librtmp) |
  464 ++------------------------------------------------------+
  465 +| App(Server/Client application) |
  466 ++------------------------------------------------------+
  467 +| RTMP(Protocol stack) |
  468 ++------------------------------------------------------+
  469 +| Kernel(depends on Core, provides error/log) |
  470 ++------------------------------------------------------+
  471 +| Core(depends only on system apis) |
  472 ++------------------------------------------------------+
  473 +</pre>
  474 +
  475 +### Stream Architecture
  476 +
  477 +<pre>
  478 + +---------+ +----------+
  479 + + Publish + + Deliver |
  480 + +---|-----+ +----|-----+
  481 ++----------------------+-------------------------+----------------+
  482 +| Input | SRS(Simple RTMP Server) | Output |
  483 ++----------------------+-------------------------+----------------+
  484 +| Encoder(1) | +-> RTMP protocol ----+-> Flash Player |
  485 +| (FMLE,FFMPEG, -rtmp-+->-+-> HLS/NGINX --------+-> m3u8 player |
  486 +| Flash,XSPLIT, | +-> Fowarder ---------+-> RTMP Server |
  487 +| ......) | +-> Transcoder -------+-> RTMP Server |
  488 +| | +-> DVR --------------+-> FILE |
  489 +| | +-> BandwidthTest ----+-> Flash/StLoad |
  490 ++----------------------+ | |
  491 +| MediaSource(2) | | |
  492 +| (RTSP,FILE, | | |
  493 +| HTTP,HLS, ------+->-- Ingester ----(rtmp)-+-> SRS |
  494 +| Device, | | |
  495 +| ......) | | |
  496 ++----------------------+-------------------------+----------------+
  497 +
  498 +Remark:
  499 +(1) Encoder: encoder must push RTMP stream to SRS server.
  500 +(2) MediaSource: any media source, which can be ingest by ffmpeg.
  501 +(3) Ingester: SRS will fork a process to run ffmpeg(or your application)
  502 +to ingest any input to rtmp, push to SRS.
  503 +</pre>
  504 +
  505 +### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC)
  506 +
  507 +<pre>
  508 + +----------+ +----------+
  509 + +--ATC->-+ server +--ATC->-+ packager +-+ +---------+
  510 ++----------+ | RTMP +----------+ RTMP +----------+ | | Reverse | +-------+
  511 +| encoder +->-+ +->-+ Proxy +-->-+ CDN +
  512 ++----------+ | +----------+ +----------+ | | (nginx) | +-------+
  513 + +--ATC->-+ server +--ATC->-+ packager +-+ +---------+
  514 + RTMP +----------+ RTMP +----------+
  515 +</pre>
  516 +
  517 +### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge)
  518 +
  519 +Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge)
  520 +Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward)
  521 +
  522 +<pre>
  523 ++---------+ +-----------------+ +-----------------------+
  524 ++ Encoder +--+-->-+ SRS(RTMP Edge) +--->-+ (RTMP Origin) |
  525 ++---------+ | +-----------------+ | SRS/FMS/NGINX-RTMP |
  526 + | | Red5/HELIX/CRTMP |
  527 + +-------------------------->-+ ...... |
  528 + +-----------------------+
  529 +Schema#1: Any RTMP encoder push RTMP stream to RTMP (origin/edge)server,
  530 + where SRS RTMP Edge server will forward stream to origin.
  531 +
  532 +
  533 ++-------------+ +-----------------+ +--------------------+
  534 +| RTMP Origin +-->-+ SRS(RTMP Edge) +--+->-+ Client(RTMP/HLS) |
  535 ++-------------+ +-----------------+ | | Flash/IOS/Android |
  536 + | +--------------------+
  537 + |
  538 + | +-----------------+
  539 + +->-+ SRS(RTMP Edge) +
  540 + +-----------------+
  541 +Schema#2: SRS RTMP Edge server pull stream from origin (or upstream SRS
  542 + RTMP Edge server), then delivery to Client.
  543 +</pre>
  544 +
  545 +### (plan) SRS Multiple processes Architecture(design by wenjie)
  546 +
  547 +<pre>
  548 + +---------------+ +--------+
  549 + | upnode server | + client +
  550 + +-------+-------+ +---+----+
  551 + -------------+------------network-------+---------
  552 + | |
  553 + +--------+ +----+-----------+ +----+----------+
  554 + | master +--fork->-+ back source(1) +-->-pull-+ stream 1-N(2) +
  555 + +---+----+ +----------------+ +-------+-------+
  556 + | |
  557 + +-------------------------------------fork--->-----+
  558 + | +-------------+
  559 + +-------------------fork-->-+ http/vod(3) |
  560 + +-------------+
  561 +Remark:
  562 +(1) back source process: create by master process, get stream from
  563 + upnode server if edge, create stream if origin, serve the stream
  564 + process.
  565 +(2) stream process: create by master process, get stream from back
  566 + source process, serve the client.
  567 +(3) the embeded mininum http server, also provides vod service. for
  568 + http server, it provides http api, hls(live/vod) delivery. for
  569 + vod server, it slice the file to hls(m3u8/ts).
  570 +Remark:
  571 +(a) This multiple processes architecture is design by wenjie, it's a
  572 + very simple and powerful multiple process architecture, for the
  573 + master no need to pass between stream process.
  574 +(b) The CLI architecture is similar to this, instead, cli process
  575 + will collect informations from all stream process, master process
  576 + only send signals to child processes.
  577 +</pre>
  578 +
  579 +### (plan) CLI Architecture
  580 +
  581 +<pre>
  582 + +---------+
  583 + +--+ stream1 +---------+
  584 + | +---------+ |
  585 + +--------+ | +---------+ | +-------+
  586 + | master +--fork->-+--+ streamN +---amf0--+>--+ cli +
  587 + +--------+ | +---------+ | +-------+
  588 + | +-------------+ |
  589 + +--+ back source +-----+
  590 + +-------------+
  591 +Remark:
  592 +(1) master listen the global api port, for example, 33330
  593 +(2) back source and stream processes listen at private api port,
  594 + for example, 33331, 33332, 33333
  595 +(3) work processes(stream and back-source), report private api
  596 + port to master global api port.
  597 +(4) cli connect to master global api port, get all other private
  598 + api ports
  599 +(5) cli connect to each stream/back-source process to get api data,
  600 + cli analysis and summary the data, return to user.
  601 +</pre>
  602 +
  603 +### Bandwidth Test Workflow
  604 +
  605 +<pre>
  606 + +------------+ +----------+
  607 + | Client | | Server |
  608 + +-----+------+ +-----+----+
  609 + | |
  610 + | connect vhost-------------> |
  611 + | &lt;-----------result(success) |
  612 + | |
  613 + | &lt;----------call(start play) |
  614 + | result(playing)----------> |
  615 + | &lt;-------------data(playing) |
  616 + | &lt;-----------call(stop play) |
  617 + | result(stopped)----------> |
  618 + | |
  619 + | &lt;-------call(start publish) |
  620 + | result(publishing)-------> |
  621 + | data(publishing)---------> |
  622 + | &lt;--------call(stop publish) |
  623 + | result(stopped)(1)-------> |
  624 + | |
  625 + | &lt;--------------------report |
  626 + | final(2)-----------------> |
  627 + | &lt;END> |
  628 +
  629 +@See: class SrsBandwidth comments.
  630 +</pre>
  631 +
  632 +Beijing, 2013.10<br/>
  633 +Winlin
  634 +
  635 +
不能预览此文件类型
不能预览此文件类型
  1 +#!/bin/bash
  2 +
  3 +# check exists.
  4 +if [[ -f /usr/local/bin/ccache ]]; then
  5 + echo "ccache is ok";
  6 + exit 0;
  7 +fi
  8 +
  9 +# check sudoer.
  10 +sudo echo "ok" > /dev/null 2>&1;
  11 +ret=$?; if [[ 0 -ne ${ret} ]]; then echo "you must be sudoer"; exit 1; fi
  12 +
  13 +unzip ccache-3.1.9.zip && cd ccache-3.1.9 && ./configure && make
  14 +ret=$?; if [[ $ret -ne 0 ]]; then echo "build ccache failed."; exit $ret; fi
  15 +
  16 +sudo cp ccache /usr/local/bin && sudo ln -s ccache /usr/local/bin/gcc && sudo ln -s ccache /usr/local/bin/g++ && sudo ln -s ccache /usr/local/bin/cc && sudo ln -s ccache /usr/local/bin/c++
  17 +ret=$?; if [[ $ret -ne 0 ]]; then echo "install ccache failed."; exit $ret; fi
不能预览此文件类型
  1 +ccache是samba组织提供的加速编译过程的工具,
  2 +使用虚拟机编译可以考虑用这个工具,让编译过程飞快。
  3 +
  4 +链接:
  5 + http://ccache.samba.org/
  6 + http://samba.org/ftp/ccache/ccache-3.1.9.tar.xz
  7 + http://ccache.samba.org/manual.html
  8 +
  9 +安装方法:
  10 + bash build_ccache.sh
  11 +注意:要求以sudoer执行,要修改文件。
不能预览此文件类型
不能预览此文件类型
  1 +#!/bin/bash
  2 +
  3 +# check exists.
  4 +if [[ -d graphviz-2.18 ]]; then
  5 + echo "graphviz is ok";
  6 + exit 0;
  7 +fi
  8 +
  9 +# check sudoer.
  10 +sudo echo "ok" > /dev/null 2>&1;
  11 +ret=$?; if [[ 0 -ne ${ret} ]]; then echo "you must be sudoer"; exit 1; fi
  12 +
  13 +unzip -q graphviz-2.36.0.zip
  14 +cd graphviz-2.36.0 && ./configure && make && sudo make install
  15 +ret=$?; if [[ $ret -ne 0 ]]; then echo "build gprof2dot failed."; exit $ret; fi
  16 +
  17 +echo "we test in Centos6.0, it's ok"
  1 +#!/usr/bin/env python
  2 +#
  3 +# Copyright 2008-2009 Jose Fonseca
  4 +#
  5 +# This program is free software: you can redistribute it and/or modify it
  6 +# under the terms of the GNU Lesser General Public License as published
  7 +# by the Free Software Foundation, either version 3 of the License, or
  8 +# (at your option) any later version.
  9 +#
  10 +# This program is distributed in the hope that it will be useful,
  11 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 +# GNU Lesser General Public License for more details.
  14 +#
  15 +# You should have received a copy of the GNU Lesser General Public License
  16 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  17 +#
  18 +
  19 +"""Generate a dot graph from the output of several profilers."""
  20 +
  21 +__author__ = "Jose Fonseca"
  22 +
  23 +__version__ = "1.0"
  24 +
  25 +
  26 +import sys
  27 +import math
  28 +import os.path
  29 +import re
  30 +import textwrap
  31 +import optparse
  32 +import xml.parsers.expat
  33 +
  34 +
  35 +try:
  36 + # Debugging helper module
  37 + import debug
  38 +except ImportError:
  39 + pass
  40 +
  41 +
  42 +def percentage(p):
  43 + return "%.02f%%" % (p*100.0,)
  44 +
  45 +def add(a, b):
  46 + return a + b
  47 +
  48 +def equal(a, b):
  49 + if a == b:
  50 + return a
  51 + else:
  52 + return None
  53 +
  54 +def fail(a, b):
  55 + assert False
  56 +
  57 +
  58 +tol = 2 ** -23
  59 +
  60 +def ratio(numerator, denominator):
  61 + try:
  62 + ratio = float(numerator)/float(denominator)
  63 + except ZeroDivisionError:
  64 + # 0/0 is undefined, but 1.0 yields more useful results
  65 + return 1.0
  66 + if ratio < 0.0:
  67 + if ratio < -tol:
  68 + sys.stderr.write('warning: negative ratio (%s/%s)\n' % (numerator, denominator))
  69 + return 0.0
  70 + if ratio > 1.0:
  71 + if ratio > 1.0 + tol:
  72 + sys.stderr.write('warning: ratio greater than one (%s/%s)\n' % (numerator, denominator))
  73 + return 1.0
  74 + return ratio
  75 +
  76 +
  77 +class UndefinedEvent(Exception):
  78 + """Raised when attempting to get an event which is undefined."""
  79 +
  80 + def __init__(self, event):
  81 + Exception.__init__(self)
  82 + self.event = event
  83 +
  84 + def __str__(self):
  85 + return 'unspecified event %s' % self.event.name
  86 +
  87 +
  88 +class Event(object):
  89 + """Describe a kind of event, and its basic operations."""
  90 +
  91 + def __init__(self, name, null, aggregator, formatter = str):
  92 + self.name = name
  93 + self._null = null
  94 + self._aggregator = aggregator
  95 + self._formatter = formatter
  96 +
  97 + def __eq__(self, other):
  98 + return self is other
  99 +
  100 + def __hash__(self):
  101 + return id(self)
  102 +
  103 + def null(self):
  104 + return self._null
  105 +
  106 + def aggregate(self, val1, val2):
  107 + """Aggregate two event values."""
  108 + assert val1 is not None
  109 + assert val2 is not None
  110 + return self._aggregator(val1, val2)
  111 +
  112 + def format(self, val):
  113 + """Format an event value."""
  114 + assert val is not None
  115 + return self._formatter(val)
  116 +
  117 +
  118 +MODULE = Event("Module", None, equal)
  119 +PROCESS = Event("Process", None, equal)
  120 +
  121 +CALLS = Event("Calls", 0, add)
  122 +SAMPLES = Event("Samples", 0, add)
  123 +SAMPLES2 = Event("Samples", 0, add)
  124 +
  125 +TIME = Event("Time", 0.0, add, lambda x: '(' + str(x) + ')')
  126 +TIME_RATIO = Event("Time ratio", 0.0, add, lambda x: '(' + percentage(x) + ')')
  127 +TOTAL_TIME = Event("Total time", 0.0, fail)
  128 +TOTAL_TIME_RATIO = Event("Total time ratio", 0.0, fail, percentage)
  129 +
  130 +CALL_RATIO = Event("Call ratio", 0.0, add, percentage)
  131 +
  132 +PRUNE_RATIO = Event("Prune ratio", 0.0, add, percentage)
  133 +
  134 +
  135 +class Object(object):
  136 + """Base class for all objects in profile which can store events."""
  137 +
  138 + def __init__(self, events=None):
  139 + if events is None:
  140 + self.events = {}
  141 + else:
  142 + self.events = events
  143 +
  144 + def __hash__(self):
  145 + return id(self)
  146 +
  147 + def __eq__(self, other):
  148 + return self is other
  149 +
  150 + def __contains__(self, event):
  151 + return event in self.events
  152 +
  153 + def __getitem__(self, event):
  154 + try:
  155 + return self.events[event]
  156 + except KeyError:
  157 + raise UndefinedEvent(event)
  158 +
  159 + def __setitem__(self, event, value):
  160 + if value is None:
  161 + if event in self.events:
  162 + del self.events[event]
  163 + else:
  164 + self.events[event] = value
  165 +
  166 +
  167 +class Call(Object):
  168 + """A call between functions.
  169 +
  170 + There should be at most one call object for every pair of functions.
  171 + """
  172 +
  173 + def __init__(self, callee_id):
  174 + Object.__init__(self)
  175 + self.callee_id = callee_id
  176 +
  177 +
  178 +class Function(Object):
  179 + """A function."""
  180 +
  181 + def __init__(self, id, name):
  182 + Object.__init__(self)
  183 + self.id = id
  184 + self.name = name
  185 + self.calls = {}
  186 + self.cycle = None
  187 +
  188 + def add_call(self, call):
  189 + if call.callee_id in self.calls:
  190 + sys.stderr.write('warning: overwriting call from function %s to %s\n' % (str(self.id), str(call.callee_id)))
  191 + self.calls[call.callee_id] = call
  192 +
  193 + # TODO: write utility functions
  194 +
  195 + def __repr__(self):
  196 + return self.name
  197 +
  198 +
  199 +class Cycle(Object):
  200 + """A cycle made from recursive function calls."""
  201 +
  202 + def __init__(self):
  203 + Object.__init__(self)
  204 + # XXX: Do cycles need an id?
  205 + self.functions = set()
  206 +
  207 + def add_function(self, function):
  208 + assert function not in self.functions
  209 + self.functions.add(function)
  210 + # XXX: Aggregate events?
  211 + if function.cycle is not None:
  212 + for other in function.cycle.functions:
  213 + if function not in self.functions:
  214 + self.add_function(other)
  215 + function.cycle = self
  216 +
  217 +
  218 +class Profile(Object):
  219 + """The whole profile."""
  220 +
  221 + def __init__(self):
  222 + Object.__init__(self)
  223 + self.functions = {}
  224 + self.cycles = []
  225 +
  226 + def add_function(self, function):
  227 + if function.id in self.functions:
  228 + sys.stderr.write('warning: overwriting function %s (id %s)\n' % (function.name, str(function.id)))
  229 + self.functions[function.id] = function
  230 +
  231 + def add_cycle(self, cycle):
  232 + self.cycles.append(cycle)
  233 +
  234 + def validate(self):
  235 + """Validate the edges."""
  236 +
  237 + for function in self.functions.itervalues():
  238 + for callee_id in function.calls.keys():
  239 + assert function.calls[callee_id].callee_id == callee_id
  240 + if callee_id not in self.functions:
  241 + sys.stderr.write('warning: call to undefined function %s from function %s\n' % (str(callee_id), function.name))
  242 + del function.calls[callee_id]
  243 +
  244 + def find_cycles(self):
  245 + """Find cycles using Tarjan's strongly connected components algorithm."""
  246 +
  247 + # Apply the Tarjan's algorithm successively until all functions are visited
  248 + visited = set()
  249 + for function in self.functions.itervalues():
  250 + if function not in visited:
  251 + self._tarjan(function, 0, [], {}, {}, visited)
  252 + cycles = []
  253 + for function in self.functions.itervalues():
  254 + if function.cycle is not None and function.cycle not in cycles:
  255 + cycles.append(function.cycle)
  256 + self.cycles = cycles
  257 + if 0:
  258 + for cycle in cycles:
  259 + sys.stderr.write("Cycle:\n")
  260 + for member in cycle.functions:
  261 + sys.stderr.write("\tFunction %s\n" % member.name)
  262 +
  263 + def _tarjan(self, function, order, stack, orders, lowlinks, visited):
  264 + """Tarjan's strongly connected components algorithm.
  265 +
  266 + See also:
  267 + - http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
  268 + """
  269 +
  270 + visited.add(function)
  271 + orders[function] = order
  272 + lowlinks[function] = order
  273 + order += 1
  274 + pos = len(stack)
  275 + stack.append(function)
  276 + for call in function.calls.itervalues():
  277 + callee = self.functions[call.callee_id]
  278 + # TODO: use a set to optimize lookup
  279 + if callee not in orders:
  280 + order = self._tarjan(callee, order, stack, orders, lowlinks, visited)
  281 + lowlinks[function] = min(lowlinks[function], lowlinks[callee])
  282 + elif callee in stack:
  283 + lowlinks[function] = min(lowlinks[function], orders[callee])
  284 + if lowlinks[function] == orders[function]:
  285 + # Strongly connected component found
  286 + members = stack[pos:]
  287 + del stack[pos:]
  288 + if len(members) > 1:
  289 + cycle = Cycle()
  290 + for member in members:
  291 + cycle.add_function(member)
  292 + return order
  293 +
  294 + def call_ratios(self, event):
  295 + # Aggregate for incoming calls
  296 + cycle_totals = {}
  297 + for cycle in self.cycles:
  298 + cycle_totals[cycle] = 0.0
  299 + function_totals = {}
  300 + for function in self.functions.itervalues():
  301 + function_totals[function] = 0.0
  302 + for function in self.functions.itervalues():
  303 + for call in function.calls.itervalues():
  304 + if call.callee_id != function.id:
  305 + callee = self.functions[call.callee_id]
  306 + function_totals[callee] += call[event]
  307 + if callee.cycle is not None and callee.cycle is not function.cycle:
  308 + cycle_totals[callee.cycle] += call[event]
  309 +
  310 + # Compute the ratios
  311 + for function in self.functions.itervalues():
  312 + for call in function.calls.itervalues():
  313 + assert CALL_RATIO not in call
  314 + if call.callee_id != function.id:
  315 + callee = self.functions[call.callee_id]
  316 + if callee.cycle is not None and callee.cycle is not function.cycle:
  317 + total = cycle_totals[callee.cycle]
  318 + else:
  319 + total = function_totals[callee]
  320 + call[CALL_RATIO] = ratio(call[event], total)
  321 +
  322 + def integrate(self, outevent, inevent):
  323 + """Propagate function time ratio allong the function calls.
  324 +
  325 + Must be called after finding the cycles.
  326 +
  327 + See also:
  328 + - http://citeseer.ist.psu.edu/graham82gprof.html
  329 + """
  330 +
  331 + # Sanity checking
  332 + assert outevent not in self
  333 + for function in self.functions.itervalues():
  334 + assert outevent not in function
  335 + assert inevent in function
  336 + for call in function.calls.itervalues():
  337 + assert outevent not in call
  338 + if call.callee_id != function.id:
  339 + assert CALL_RATIO in call
  340 +
  341 + # Aggregate the input for each cycle
  342 + for cycle in self.cycles:
  343 + total = inevent.null()
  344 + for function in self.functions.itervalues():
  345 + total = inevent.aggregate(total, function[inevent])
  346 + self[inevent] = total
  347 +
  348 + # Integrate along the edges
  349 + total = inevent.null()
  350 + for function in self.functions.itervalues():
  351 + total = inevent.aggregate(total, function[inevent])
  352 + self._integrate_function(function, outevent, inevent)
  353 + self[outevent] = total
  354 +
  355 + def _integrate_function(self, function, outevent, inevent):
  356 + if function.cycle is not None:
  357 + return self._integrate_cycle(function.cycle, outevent, inevent)
  358 + else:
  359 + if outevent not in function:
  360 + total = function[inevent]
  361 + for call in function.calls.itervalues():
  362 + if call.callee_id != function.id:
  363 + total += self._integrate_call(call, outevent, inevent)
  364 + function[outevent] = total
  365 + return function[outevent]
  366 +
  367 + def _integrate_call(self, call, outevent, inevent):
  368 + assert outevent not in call
  369 + assert CALL_RATIO in call
  370 + callee = self.functions[call.callee_id]
  371 + subtotal = call[CALL_RATIO]*self._integrate_function(callee, outevent, inevent)
  372 + call[outevent] = subtotal
  373 + return subtotal
  374 +
  375 + def _integrate_cycle(self, cycle, outevent, inevent):
  376 + if outevent not in cycle:
  377 +
  378 + # Compute the outevent for the whole cycle
  379 + total = inevent.null()
  380 + for member in cycle.functions:
  381 + subtotal = member[inevent]
  382 + for call in member.calls.itervalues():
  383 + callee = self.functions[call.callee_id]
  384 + if callee.cycle is not cycle:
  385 + subtotal += self._integrate_call(call, outevent, inevent)
  386 + total += subtotal
  387 + cycle[outevent] = total
  388 +
  389 + # Compute the time propagated to callers of this cycle
  390 + callees = {}
  391 + for function in self.functions.itervalues():
  392 + if function.cycle is not cycle:
  393 + for call in function.calls.itervalues():
  394 + callee = self.functions[call.callee_id]
  395 + if callee.cycle is cycle:
  396 + try:
  397 + callees[callee] += call[CALL_RATIO]
  398 + except KeyError:
  399 + callees[callee] = call[CALL_RATIO]
  400 +
  401 + for member in cycle.functions:
  402 + member[outevent] = outevent.null()
  403 +
  404 + for callee, call_ratio in callees.iteritems():
  405 + ranks = {}
  406 + call_ratios = {}
  407 + partials = {}
  408 + self._rank_cycle_function(cycle, callee, 0, ranks)
  409 + self._call_ratios_cycle(cycle, callee, ranks, call_ratios, set())
  410 + partial = self._integrate_cycle_function(cycle, callee, call_ratio, partials, ranks, call_ratios, outevent, inevent)
  411 + assert partial == max(partials.values())
  412 + assert not total or abs(1.0 - partial/(call_ratio*total)) <= 0.001
  413 +
  414 + return cycle[outevent]
  415 +
  416 + def _rank_cycle_function(self, cycle, function, rank, ranks):
  417 + if function not in ranks or ranks[function] > rank:
  418 + ranks[function] = rank
  419 + for call in function.calls.itervalues():
  420 + if call.callee_id != function.id:
  421 + callee = self.functions[call.callee_id]
  422 + if callee.cycle is cycle:
  423 + self._rank_cycle_function(cycle, callee, rank + 1, ranks)
  424 +
  425 + def _call_ratios_cycle(self, cycle, function, ranks, call_ratios, visited):
  426 + if function not in visited:
  427 + visited.add(function)
  428 + for call in function.calls.itervalues():
  429 + if call.callee_id != function.id:
  430 + callee = self.functions[call.callee_id]
  431 + if callee.cycle is cycle:
  432 + if ranks[callee] > ranks[function]:
  433 + call_ratios[callee] = call_ratios.get(callee, 0.0) + call[CALL_RATIO]
  434 + self._call_ratios_cycle(cycle, callee, ranks, call_ratios, visited)
  435 +
  436 + def _integrate_cycle_function(self, cycle, function, partial_ratio, partials, ranks, call_ratios, outevent, inevent):
  437 + if function not in partials:
  438 + partial = partial_ratio*function[inevent]
  439 + for call in function.calls.itervalues():
  440 + if call.callee_id != function.id:
  441 + callee = self.functions[call.callee_id]
  442 + if callee.cycle is not cycle:
  443 + assert outevent in call
  444 + partial += partial_ratio*call[outevent]
  445 + else:
  446 + if ranks[callee] > ranks[function]:
  447 + callee_partial = self._integrate_cycle_function(cycle, callee, partial_ratio, partials, ranks, call_ratios, outevent, inevent)
  448 + call_ratio = ratio(call[CALL_RATIO], call_ratios[callee])
  449 + call_partial = call_ratio*callee_partial
  450 + try:
  451 + call[outevent] += call_partial
  452 + except UndefinedEvent:
  453 + call[outevent] = call_partial
  454 + partial += call_partial
  455 + partials[function] = partial
  456 + try:
  457 + function[outevent] += partial
  458 + except UndefinedEvent:
  459 + function[outevent] = partial
  460 + return partials[function]
  461 +
  462 + def aggregate(self, event):
  463 + """Aggregate an event for the whole profile."""
  464 +
  465 + total = event.null()
  466 + for function in self.functions.itervalues():
  467 + try:
  468 + total = event.aggregate(total, function[event])
  469 + except UndefinedEvent:
  470 + return
  471 + self[event] = total
  472 +
  473 + def ratio(self, outevent, inevent):
  474 + assert outevent not in self
  475 + assert inevent in self
  476 + for function in self.functions.itervalues():
  477 + assert outevent not in function
  478 + assert inevent in function
  479 + function[outevent] = ratio(function[inevent], self[inevent])
  480 + for call in function.calls.itervalues():
  481 + assert outevent not in call
  482 + if inevent in call:
  483 + call[outevent] = ratio(call[inevent], self[inevent])
  484 + self[outevent] = 1.0
  485 +
  486 + def prune(self, node_thres, edge_thres):
  487 + """Prune the profile"""
  488 +
  489 + # compute the prune ratios
  490 + for function in self.functions.itervalues():
  491 + try:
  492 + function[PRUNE_RATIO] = function[TOTAL_TIME_RATIO]
  493 + except UndefinedEvent:
  494 + pass
  495 +
  496 + for call in function.calls.itervalues():
  497 + callee = self.functions[call.callee_id]
  498 +
  499 + if TOTAL_TIME_RATIO in call:
  500 + # handle exact cases first
  501 + call[PRUNE_RATIO] = call[TOTAL_TIME_RATIO]
  502 + else:
  503 + try:
  504 + # make a safe estimate
  505 + call[PRUNE_RATIO] = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO])
  506 + except UndefinedEvent:
  507 + pass
  508 +
  509 + # prune the nodes
  510 + for function_id in self.functions.keys():
  511 + function = self.functions[function_id]
  512 + try:
  513 + if function[PRUNE_RATIO] < node_thres:
  514 + del self.functions[function_id]
  515 + except UndefinedEvent:
  516 + pass
  517 +
  518 + # prune the egdes
  519 + for function in self.functions.itervalues():
  520 + for callee_id in function.calls.keys():
  521 + call = function.calls[callee_id]
  522 + try:
  523 + if callee_id not in self.functions or call[PRUNE_RATIO] < edge_thres:
  524 + del function.calls[callee_id]
  525 + except UndefinedEvent:
  526 + pass
  527 +
  528 + def dump(self):
  529 + for function in self.functions.itervalues():
  530 + sys.stderr.write('Function %s:\n' % (function.name,))
  531 + self._dump_events(function.events)
  532 + for call in function.calls.itervalues():
  533 + callee = self.functions[call.callee_id]
  534 + sys.stderr.write(' Call %s:\n' % (callee.name,))
  535 + self._dump_events(call.events)
  536 + for cycle in self.cycles:
  537 + sys.stderr.write('Cycle:\n')
  538 + self._dump_events(cycle.events)
  539 + for function in cycle.functions:
  540 + sys.stderr.write(' Function %s\n' % (function.name,))
  541 +
  542 + def _dump_events(self, events):
  543 + for event, value in events.iteritems():
  544 + sys.stderr.write(' %s: %s\n' % (event.name, event.format(value)))
  545 +
  546 +
  547 +class Struct:
  548 + """Masquerade a dictionary with a structure-like behavior."""
  549 +
  550 + def __init__(self, attrs = None):
  551 + if attrs is None:
  552 + attrs = {}
  553 + self.__dict__['_attrs'] = attrs
  554 +
  555 + def __getattr__(self, name):
  556 + try:
  557 + return self._attrs[name]
  558 + except KeyError:
  559 + raise AttributeError(name)
  560 +
  561 + def __setattr__(self, name, value):
  562 + self._attrs[name] = value
  563 +
  564 + def __str__(self):
  565 + return str(self._attrs)
  566 +
  567 + def __repr__(self):
  568 + return repr(self._attrs)
  569 +
  570 +
  571 +class ParseError(Exception):
  572 + """Raised when parsing to signal mismatches."""
  573 +
  574 + def __init__(self, msg, line):
  575 + self.msg = msg
  576 + # TODO: store more source line information
  577 + self.line = line
  578 +
  579 + def __str__(self):
  580 + return '%s: %r' % (self.msg, self.line)
  581 +
  582 +
  583 +class Parser:
  584 + """Parser interface."""
  585 +
  586 + def __init__(self):
  587 + pass
  588 +
  589 + def parse(self):
  590 + raise NotImplementedError
  591 +
  592 +
  593 +class LineParser(Parser):
  594 + """Base class for parsers that read line-based formats."""
  595 +
  596 + def __init__(self, file):
  597 + Parser.__init__(self)
  598 + self._file = file
  599 + self.__line = None
  600 + self.__eof = False
  601 +
  602 + def readline(self):
  603 + line = self._file.readline()
  604 + if not line:
  605 + self.__line = ''
  606 + self.__eof = True
  607 + self.__line = line.rstrip('\r\n')
  608 +
  609 + def lookahead(self):
  610 + assert self.__line is not None
  611 + return self.__line
  612 +
  613 + def consume(self):
  614 + assert self.__line is not None
  615 + line = self.__line
  616 + self.readline()
  617 + return line
  618 +
  619 + def eof(self):
  620 + assert self.__line is not None
  621 + return self.__eof
  622 +
  623 +
  624 +XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF = range(4)
  625 +
  626 +
  627 +class XmlToken:
  628 +
  629 + def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
  630 + assert type in (XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF)
  631 + self.type = type
  632 + self.name_or_data = name_or_data
  633 + self.attrs = attrs
  634 + self.line = line
  635 + self.column = column
  636 +
  637 + def __str__(self):
  638 + if self.type == XML_ELEMENT_START:
  639 + return '<' + self.name_or_data + ' ...>'
  640 + if self.type == XML_ELEMENT_END:
  641 + return '</' + self.name_or_data + '>'
  642 + if self.type == XML_CHARACTER_DATA:
  643 + return self.name_or_data
  644 + if self.type == XML_EOF:
  645 + return 'end of file'
  646 + assert 0
  647 +
  648 +
  649 +class XmlTokenizer:
  650 + """Expat based XML tokenizer."""
  651 +
  652 + def __init__(self, fp, skip_ws = True):
  653 + self.fp = fp
  654 + self.tokens = []
  655 + self.index = 0
  656 + self.final = False
  657 + self.skip_ws = skip_ws
  658 +
  659 + self.character_pos = 0, 0
  660 + self.character_data = ''
  661 +
  662 + self.parser = xml.parsers.expat.ParserCreate()
  663 + self.parser.StartElementHandler = self.handle_element_start
  664 + self.parser.EndElementHandler = self.handle_element_end
  665 + self.parser.CharacterDataHandler = self.handle_character_data
  666 +
  667 + def handle_element_start(self, name, attributes):
  668 + self.finish_character_data()
  669 + line, column = self.pos()
  670 + token = XmlToken(XML_ELEMENT_START, name, attributes, line, column)
  671 + self.tokens.append(token)
  672 +
  673 + def handle_element_end(self, name):
  674 + self.finish_character_data()
  675 + line, column = self.pos()
  676 + token = XmlToken(XML_ELEMENT_END, name, None, line, column)
  677 + self.tokens.append(token)
  678 +
  679 + def handle_character_data(self, data):
  680 + if not self.character_data:
  681 + self.character_pos = self.pos()
  682 + self.character_data += data
  683 +
  684 + def finish_character_data(self):
  685 + if self.character_data:
  686 + if not self.skip_ws or not self.character_data.isspace():
  687 + line, column = self.character_pos
  688 + token = XmlToken(XML_CHARACTER_DATA, self.character_data, None, line, column)
  689 + self.tokens.append(token)
  690 + self.character_data = ''
  691 +
  692 + def next(self):
  693 + size = 16*1024
  694 + while self.index >= len(self.tokens) and not self.final:
  695 + self.tokens = []
  696 + self.index = 0
  697 + data = self.fp.read(size)
  698 + self.final = len(data) < size
  699 + try:
  700 + self.parser.Parse(data, self.final)
  701 + except xml.parsers.expat.ExpatError, e:
  702 + #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
  703 + if e.code == 3:
  704 + pass
  705 + else:
  706 + raise e
  707 + if self.index >= len(self.tokens):
  708 + line, column = self.pos()
  709 + token = XmlToken(XML_EOF, None, None, line, column)
  710 + else:
  711 + token = self.tokens[self.index]
  712 + self.index += 1
  713 + return token
  714 +
  715 + def pos(self):
  716 + return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
  717 +
  718 +
  719 +class XmlTokenMismatch(Exception):
  720 +
  721 + def __init__(self, expected, found):
  722 + self.expected = expected
  723 + self.found = found
  724 +
  725 + def __str__(self):
  726 + return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
  727 +
  728 +
  729 +class XmlParser(Parser):
  730 + """Base XML document parser."""
  731 +
  732 + def __init__(self, fp):
  733 + Parser.__init__(self)
  734 + self.tokenizer = XmlTokenizer(fp)
  735 + self.consume()
  736 +
  737 + def consume(self):
  738 + self.token = self.tokenizer.next()
  739 +
  740 + def match_element_start(self, name):
  741 + return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name
  742 +
  743 + def match_element_end(self, name):
  744 + return self.token.type == XML_ELEMENT_END and self.token.name_or_data == name
  745 +
  746 + def element_start(self, name):
  747 + while self.token.type == XML_CHARACTER_DATA:
  748 + self.consume()
  749 + if self.token.type != XML_ELEMENT_START:
  750 + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token)
  751 + if self.token.name_or_data != name:
  752 + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token)
  753 + attrs = self.token.attrs
  754 + self.consume()
  755 + return attrs
  756 +
  757 + def element_end(self, name):
  758 + while self.token.type == XML_CHARACTER_DATA:
  759 + self.consume()
  760 + if self.token.type != XML_ELEMENT_END:
  761 + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token)
  762 + if self.token.name_or_data != name:
  763 + raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token)
  764 + self.consume()
  765 +
  766 + def character_data(self, strip = True):
  767 + data = ''
  768 + while self.token.type == XML_CHARACTER_DATA:
  769 + data += self.token.name_or_data
  770 + self.consume()
  771 + if strip:
  772 + data = data.strip()
  773 + return data
  774 +
  775 +
  776 +class GprofParser(Parser):
  777 + """Parser for GNU gprof output.
  778 +
  779 + See also:
  780 + - Chapter "Interpreting gprof's Output" from the GNU gprof manual
  781 + http://sourceware.org/binutils/docs-2.18/gprof/Call-Graph.html#Call-Graph
  782 + - File "cg_print.c" from the GNU gprof source code
  783 + http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/src/gprof/cg_print.c?rev=1.12&cvsroot=src
  784 + """
  785 +
  786 + def __init__(self, fp):
  787 + Parser.__init__(self)
  788 + self.fp = fp
  789 + self.functions = {}
  790 + self.cycles = {}
  791 +
  792 + def readline(self):
  793 + line = self.fp.readline()
  794 + if not line:
  795 + sys.stderr.write('error: unexpected end of file\n')
  796 + sys.exit(1)
  797 + line = line.rstrip('\r\n')
  798 + return line
  799 +
  800 + _int_re = re.compile(r'^\d+$')
  801 + _float_re = re.compile(r'^\d+\.\d+$')
  802 +
  803 + def translate(self, mo):
  804 + """Extract a structure from a match object, while translating the types in the process."""
  805 + attrs = {}
  806 + groupdict = mo.groupdict()
  807 + for name, value in groupdict.iteritems():
  808 + if value is None:
  809 + value = None
  810 + elif self._int_re.match(value):
  811 + value = int(value)
  812 + elif self._float_re.match(value):
  813 + value = float(value)
  814 + attrs[name] = (value)
  815 + return Struct(attrs)
  816 +
  817 + _cg_header_re = re.compile(
  818 + # original gprof header
  819 + r'^\s+called/total\s+parents\s*$|' +
  820 + r'^index\s+%time\s+self\s+descendents\s+called\+self\s+name\s+index\s*$|' +
  821 + r'^\s+called/total\s+children\s*$|' +
  822 + # GNU gprof header
  823 + r'^index\s+%\s+time\s+self\s+children\s+called\s+name\s*$'
  824 + )
  825 +
  826 + _cg_ignore_re = re.compile(
  827 + # spontaneous
  828 + r'^\s+<spontaneous>\s*$|'
  829 + # internal calls (such as "mcount")
  830 + r'^.*\((\d+)\)$'
  831 + )
  832 +
  833 + _cg_primary_re = re.compile(
  834 + r'^\[(?P<index>\d+)\]?' +
  835 + r'\s+(?P<percentage_time>\d+\.\d+)' +
  836 + r'\s+(?P<self>\d+\.\d+)' +
  837 + r'\s+(?P<descendants>\d+\.\d+)' +
  838 + r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
  839 + r'\s+(?P<name>\S.*?)' +
  840 + r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
  841 + r'\s\[(\d+)\]$'
  842 + )
  843 +
  844 + _cg_parent_re = re.compile(
  845 + r'^\s+(?P<self>\d+\.\d+)?' +
  846 + r'\s+(?P<descendants>\d+\.\d+)?' +
  847 + r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' +
  848 + r'\s+(?P<name>\S.*?)' +
  849 + r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
  850 + r'\s\[(?P<index>\d+)\]$'
  851 + )
  852 +
  853 + _cg_child_re = _cg_parent_re
  854 +
  855 + _cg_cycle_header_re = re.compile(
  856 + r'^\[(?P<index>\d+)\]?' +
  857 + r'\s+(?P<percentage_time>\d+\.\d+)' +
  858 + r'\s+(?P<self>\d+\.\d+)' +
  859 + r'\s+(?P<descendants>\d+\.\d+)' +
  860 + r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
  861 + r'\s+<cycle\s(?P<cycle>\d+)\sas\sa\swhole>' +
  862 + r'\s\[(\d+)\]$'
  863 + )
  864 +
  865 + _cg_cycle_member_re = re.compile(
  866 + r'^\s+(?P<self>\d+\.\d+)?' +
  867 + r'\s+(?P<descendants>\d+\.\d+)?' +
  868 + r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' +
  869 + r'\s+(?P<name>\S.*?)' +
  870 + r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
  871 + r'\s\[(?P<index>\d+)\]$'
  872 + )
  873 +
  874 + _cg_sep_re = re.compile(r'^--+$')
  875 +
  876 + def parse_function_entry(self, lines):
  877 + parents = []
  878 + children = []
  879 +
  880 + while True:
  881 + if not lines:
  882 + sys.stderr.write('warning: unexpected end of entry\n')
  883 + line = lines.pop(0)
  884 + if line.startswith('['):
  885 + break
  886 +
  887 + # read function parent line
  888 + mo = self._cg_parent_re.match(line)
  889 + if not mo:
  890 + if self._cg_ignore_re.match(line):
  891 + continue
  892 + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line)
  893 + else:
  894 + parent = self.translate(mo)
  895 + parents.append(parent)
  896 +
  897 + # read primary line
  898 + mo = self._cg_primary_re.match(line)
  899 + if not mo:
  900 + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line)
  901 + return
  902 + else:
  903 + function = self.translate(mo)
  904 +
  905 + while lines:
  906 + line = lines.pop(0)
  907 +
  908 + # read function subroutine line
  909 + mo = self._cg_child_re.match(line)
  910 + if not mo:
  911 + if self._cg_ignore_re.match(line):
  912 + continue
  913 + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line)
  914 + else:
  915 + child = self.translate(mo)
  916 + children.append(child)
  917 +
  918 + function.parents = parents
  919 + function.children = children
  920 +
  921 + self.functions[function.index] = function
  922 +
  923 + def parse_cycle_entry(self, lines):
  924 +
  925 + # read cycle header line
  926 + line = lines[0]
  927 + mo = self._cg_cycle_header_re.match(line)
  928 + if not mo:
  929 + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line)
  930 + return
  931 + cycle = self.translate(mo)
  932 +
  933 + # read cycle member lines
  934 + cycle.functions = []
  935 + for line in lines[1:]:
  936 + mo = self._cg_cycle_member_re.match(line)
  937 + if not mo:
  938 + sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line)
  939 + continue
  940 + call = self.translate(mo)
  941 + cycle.functions.append(call)
  942 +
  943 + self.cycles[cycle.cycle] = cycle
  944 +
  945 + def parse_cg_entry(self, lines):
  946 + if lines[0].startswith("["):
  947 + self.parse_cycle_entry(lines)
  948 + else:
  949 + self.parse_function_entry(lines)
  950 +
  951 + def parse_cg(self):
  952 + """Parse the call graph."""
  953 +
  954 + # skip call graph header
  955 + while not self._cg_header_re.match(self.readline()):
  956 + pass
  957 + line = self.readline()
  958 + while self._cg_header_re.match(line):
  959 + line = self.readline()
  960 +
  961 + # process call graph entries
  962 + entry_lines = []
  963 + while line != '\014': # form feed
  964 + if line and not line.isspace():
  965 + if self._cg_sep_re.match(line):
  966 + self.parse_cg_entry(entry_lines)
  967 + entry_lines = []
  968 + else:
  969 + entry_lines.append(line)
  970 + line = self.readline()
  971 +
  972 + def parse(self):
  973 + self.parse_cg()
  974 + self.fp.close()
  975 +
  976 + profile = Profile()
  977 + profile[TIME] = 0.0
  978 +
  979 + cycles = {}
  980 + for index in self.cycles.iterkeys():
  981 + cycles[index] = Cycle()
  982 +
  983 + for entry in self.functions.itervalues():
  984 + # populate the function
  985 + function = Function(entry.index, entry.name)
  986 + function[TIME] = entry.self
  987 + if entry.called is not None:
  988 + function[CALLS] = entry.called
  989 + if entry.called_self is not None:
  990 + call = Call(entry.index)
  991 + call[CALLS] = entry.called_self
  992 + function[CALLS] += entry.called_self
  993 +
  994 + # populate the function calls
  995 + for child in entry.children:
  996 + call = Call(child.index)
  997 +
  998 + assert child.called is not None
  999 + call[CALLS] = child.called
  1000 +
  1001 + if child.index not in self.functions:
  1002 + # NOTE: functions that were never called but were discovered by gprof's
  1003 + # static call graph analysis dont have a call graph entry so we need
  1004 + # to add them here
  1005 + missing = Function(child.index, child.name)
  1006 + function[TIME] = 0.0
  1007 + function[CALLS] = 0
  1008 + profile.add_function(missing)
  1009 +
  1010 + function.add_call(call)
  1011 +
  1012 + profile.add_function(function)
  1013 +
  1014 + if entry.cycle is not None:
  1015 + try:
  1016 + cycle = cycles[entry.cycle]
  1017 + except KeyError:
  1018 + sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
  1019 + cycle = Cycle()
  1020 + cycles[entry.cycle] = cycle
  1021 + cycle.add_function(function)
  1022 +
  1023 + profile[TIME] = profile[TIME] + function[TIME]
  1024 +
  1025 + for cycle in cycles.itervalues():
  1026 + profile.add_cycle(cycle)
  1027 +
  1028 + # Compute derived events
  1029 + profile.validate()
  1030 + profile.ratio(TIME_RATIO, TIME)
  1031 + profile.call_ratios(CALLS)
  1032 + profile.integrate(TOTAL_TIME, TIME)
  1033 + profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME)
  1034 +
  1035 + return profile
  1036 +
  1037 +
  1038 +class OprofileParser(LineParser):
  1039 + """Parser for oprofile callgraph output.
  1040 +
  1041 + See also:
  1042 + - http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph
  1043 + """
  1044 +
  1045 + _fields_re = {
  1046 + 'samples': r'(?P<samples>\d+)',
  1047 + '%': r'(?P<percentage>\S+)',
  1048 + 'linenr info': r'(?P<source>\(no location information\)|\S+:\d+)',
  1049 + 'image name': r'(?P<image>\S+(?:\s\(tgid:[^)]*\))?)',
  1050 + 'app name': r'(?P<application>\S+)',
  1051 + 'symbol name': r'(?P<symbol>\(no symbols\)|.+?)',
  1052 + }
  1053 +
  1054 + def __init__(self, infile):
  1055 + LineParser.__init__(self, infile)
  1056 + self.entries = {}
  1057 + self.entry_re = None
  1058 +
  1059 + def add_entry(self, callers, function, callees):
  1060 + try:
  1061 + entry = self.entries[function.id]
  1062 + except KeyError:
  1063 + self.entries[function.id] = (callers, function, callees)
  1064 + else:
  1065 + callers_total, function_total, callees_total = entry
  1066 + self.update_subentries_dict(callers_total, callers)
  1067 + function_total.samples += function.samples
  1068 + self.update_subentries_dict(callees_total, callees)
  1069 +
  1070 + def update_subentries_dict(self, totals, partials):
  1071 + for partial in partials.itervalues():
  1072 + try:
  1073 + total = totals[partial.id]
  1074 + except KeyError:
  1075 + totals[partial.id] = partial
  1076 + else:
  1077 + total.samples += partial.samples
  1078 +
  1079 + def parse(self):
  1080 + # read lookahead
  1081 + self.readline()
  1082 +
  1083 + self.parse_header()
  1084 + while self.lookahead():
  1085 + self.parse_entry()
  1086 +
  1087 + profile = Profile()
  1088 +
  1089 + reverse_call_samples = {}
  1090 +
  1091 + # populate the profile
  1092 + profile[SAMPLES] = 0
  1093 + for _callers, _function, _callees in self.entries.itervalues():
  1094 + function = Function(_function.id, _function.name)
  1095 + function[SAMPLES] = _function.samples
  1096 + profile.add_function(function)
  1097 + profile[SAMPLES] += _function.samples
  1098 +
  1099 + if _function.application:
  1100 + function[PROCESS] = os.path.basename(_function.application)
  1101 + if _function.image:
  1102 + function[MODULE] = os.path.basename(_function.image)
  1103 +
  1104 + total_callee_samples = 0
  1105 + for _callee in _callees.itervalues():
  1106 + total_callee_samples += _callee.samples
  1107 +
  1108 + for _callee in _callees.itervalues():
  1109 + if not _callee.self:
  1110 + call = Call(_callee.id)
  1111 + call[SAMPLES2] = _callee.samples
  1112 + function.add_call(call)
  1113 +
  1114 + # compute derived data
  1115 + profile.validate()
  1116 + profile.find_cycles()
  1117 + profile.ratio(TIME_RATIO, SAMPLES)
  1118 + profile.call_ratios(SAMPLES2)
  1119 + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
  1120 +
  1121 + return profile
  1122 +
  1123 + def parse_header(self):
  1124 + while not self.match_header():
  1125 + self.consume()
  1126 + line = self.lookahead()
  1127 + fields = re.split(r'\s\s+', line)
  1128 + entry_re = r'^\s*' + r'\s+'.join([self._fields_re[field] for field in fields]) + r'(?P<self>\s+\[self\])?$'
  1129 + self.entry_re = re.compile(entry_re)
  1130 + self.skip_separator()
  1131 +
  1132 + def parse_entry(self):
  1133 + callers = self.parse_subentries()
  1134 + if self.match_primary():
  1135 + function = self.parse_subentry()
  1136 + if function is not None:
  1137 + callees = self.parse_subentries()
  1138 + self.add_entry(callers, function, callees)
  1139 + self.skip_separator()
  1140 +
  1141 + def parse_subentries(self):
  1142 + subentries = {}
  1143 + while self.match_secondary():
  1144 + subentry = self.parse_subentry()
  1145 + subentries[subentry.id] = subentry
  1146 + return subentries
  1147 +
  1148 + def parse_subentry(self):
  1149 + entry = Struct()
  1150 + line = self.consume()
  1151 + mo = self.entry_re.match(line)
  1152 + if not mo:
  1153 + raise ParseError('failed to parse', line)
  1154 + fields = mo.groupdict()
  1155 + entry.samples = int(fields.get('samples', 0))
  1156 + entry.percentage = float(fields.get('percentage', 0.0))
  1157 + if 'source' in fields and fields['source'] != '(no location information)':
  1158 + source = fields['source']
  1159 + filename, lineno = source.split(':')
  1160 + entry.filename = filename
  1161 + entry.lineno = int(lineno)
  1162 + else:
  1163 + source = ''
  1164 + entry.filename = None
  1165 + entry.lineno = None
  1166 + entry.image = fields.get('image', '')
  1167 + entry.application = fields.get('application', '')
  1168 + if 'symbol' in fields and fields['symbol'] != '(no symbols)':
  1169 + entry.symbol = fields['symbol']
  1170 + else:
  1171 + entry.symbol = ''
  1172 + if entry.symbol.startswith('"') and entry.symbol.endswith('"'):
  1173 + entry.symbol = entry.symbol[1:-1]
  1174 + entry.id = ':'.join((entry.application, entry.image, source, entry.symbol))
  1175 + entry.self = fields.get('self', None) != None
  1176 + if entry.self:
  1177 + entry.id += ':self'
  1178 + if entry.symbol:
  1179 + entry.name = entry.symbol
  1180 + else:
  1181 + entry.name = entry.image
  1182 + return entry
  1183 +
  1184 + def skip_separator(self):
  1185 + while not self.match_separator():
  1186 + self.consume()
  1187 + self.consume()
  1188 +
  1189 + def match_header(self):
  1190 + line = self.lookahead()
  1191 + return line.startswith('samples')
  1192 +
  1193 + def match_separator(self):
  1194 + line = self.lookahead()
  1195 + return line == '-'*len(line)
  1196 +
  1197 + def match_primary(self):
  1198 + line = self.lookahead()
  1199 + return not line[:1].isspace()
  1200 +
  1201 + def match_secondary(self):
  1202 + line = self.lookahead()
  1203 + return line[:1].isspace()
  1204 +
  1205 +
  1206 +class SysprofParser(XmlParser):
  1207 +
  1208 + def __init__(self, stream):
  1209 + XmlParser.__init__(self, stream)
  1210 +
  1211 + def parse(self):
  1212 + objects = {}
  1213 + nodes = {}
  1214 +
  1215 + self.element_start('profile')
  1216 + while self.token.type == XML_ELEMENT_START:
  1217 + if self.token.name_or_data == 'objects':
  1218 + assert not objects
  1219 + objects = self.parse_items('objects')
  1220 + elif self.token.name_or_data == 'nodes':
  1221 + assert not nodes
  1222 + nodes = self.parse_items('nodes')
  1223 + else:
  1224 + self.parse_value(self.token.name_or_data)
  1225 + self.element_end('profile')
  1226 +
  1227 + return self.build_profile(objects, nodes)
  1228 +
  1229 + def parse_items(self, name):
  1230 + assert name[-1] == 's'
  1231 + items = {}
  1232 + self.element_start(name)
  1233 + while self.token.type == XML_ELEMENT_START:
  1234 + id, values = self.parse_item(name[:-1])
  1235 + assert id not in items
  1236 + items[id] = values
  1237 + self.element_end(name)
  1238 + return items
  1239 +
  1240 + def parse_item(self, name):
  1241 + attrs = self.element_start(name)
  1242 + id = int(attrs['id'])
  1243 + values = self.parse_values()
  1244 + self.element_end(name)
  1245 + return id, values
  1246 +
  1247 + def parse_values(self):
  1248 + values = {}
  1249 + while self.token.type == XML_ELEMENT_START:
  1250 + name = self.token.name_or_data
  1251 + value = self.parse_value(name)
  1252 + assert name not in values
  1253 + values[name] = value
  1254 + return values
  1255 +
  1256 + def parse_value(self, tag):
  1257 + self.element_start(tag)
  1258 + value = self.character_data()
  1259 + self.element_end(tag)
  1260 + if value.isdigit():
  1261 + return int(value)
  1262 + if value.startswith('"') and value.endswith('"'):
  1263 + return value[1:-1]
  1264 + return value
  1265 +
  1266 + def build_profile(self, objects, nodes):
  1267 + profile = Profile()
  1268 +
  1269 + profile[SAMPLES] = 0
  1270 + for id, object in objects.iteritems():
  1271 + # Ignore fake objects (process names, modules, "Everything", "kernel", etc.)
  1272 + if object['self'] == 0:
  1273 + continue
  1274 +
  1275 + function = Function(id, object['name'])
  1276 + function[SAMPLES] = object['self']
  1277 + profile.add_function(function)
  1278 + profile[SAMPLES] += function[SAMPLES]
  1279 +
  1280 + for id, node in nodes.iteritems():
  1281 + # Ignore fake calls
  1282 + if node['self'] == 0:
  1283 + continue
  1284 +
  1285 + # Find a non-ignored parent
  1286 + parent_id = node['parent']
  1287 + while parent_id != 0:
  1288 + parent = nodes[parent_id]
  1289 + caller_id = parent['object']
  1290 + if objects[caller_id]['self'] != 0:
  1291 + break
  1292 + parent_id = parent['parent']
  1293 + if parent_id == 0:
  1294 + continue
  1295 +
  1296 + callee_id = node['object']
  1297 +
  1298 + assert objects[caller_id]['self']
  1299 + assert objects[callee_id]['self']
  1300 +
  1301 + function = profile.functions[caller_id]
  1302 +
  1303 + samples = node['self']
  1304 + try:
  1305 + call = function.calls[callee_id]
  1306 + except KeyError:
  1307 + call = Call(callee_id)
  1308 + call[SAMPLES2] = samples
  1309 + function.add_call(call)
  1310 + else:
  1311 + call[SAMPLES2] += samples
  1312 +
  1313 + # Compute derived events
  1314 + profile.validate()
  1315 + profile.find_cycles()
  1316 + profile.ratio(TIME_RATIO, SAMPLES)
  1317 + profile.call_ratios(SAMPLES2)
  1318 + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
  1319 +
  1320 + return profile
  1321 +
  1322 +
  1323 +class SharkParser(LineParser):
  1324 + """Parser for MacOSX Shark output.
  1325 +
  1326 + Author: tom@dbservice.com
  1327 + """
  1328 +
  1329 + def __init__(self, infile):
  1330 + LineParser.__init__(self, infile)
  1331 + self.stack = []
  1332 + self.entries = {}
  1333 +
  1334 + def add_entry(self, function):
  1335 + try:
  1336 + entry = self.entries[function.id]
  1337 + except KeyError:
  1338 + self.entries[function.id] = (function, { })
  1339 + else:
  1340 + function_total, callees_total = entry
  1341 + function_total.samples += function.samples
  1342 +
  1343 + def add_callee(self, function, callee):
  1344 + func, callees = self.entries[function.id]
  1345 + try:
  1346 + entry = callees[callee.id]
  1347 + except KeyError:
  1348 + callees[callee.id] = callee
  1349 + else:
  1350 + entry.samples += callee.samples
  1351 +
  1352 + def parse(self):
  1353 + self.readline()
  1354 + self.readline()
  1355 + self.readline()
  1356 + self.readline()
  1357 +
  1358 + match = re.compile(r'(?P<prefix>[|+ ]*)(?P<samples>\d+), (?P<symbol>[^,]+), (?P<image>.*)')
  1359 +
  1360 + while self.lookahead():
  1361 + line = self.consume()
  1362 + mo = match.match(line)
  1363 + if not mo:
  1364 + raise ParseError('failed to parse', line)
  1365 +
  1366 + fields = mo.groupdict()
  1367 + prefix = len(fields.get('prefix', 0)) / 2 - 1
  1368 +
  1369 + symbol = str(fields.get('symbol', 0))
  1370 + image = str(fields.get('image', 0))
  1371 +
  1372 + entry = Struct()
  1373 + entry.id = ':'.join([symbol, image])
  1374 + entry.samples = int(fields.get('samples', 0))
  1375 +
  1376 + entry.name = symbol
  1377 + entry.image = image
  1378 +
  1379 + # adjust the callstack
  1380 + if prefix < len(self.stack):
  1381 + del self.stack[prefix:]
  1382 +
  1383 + if prefix == len(self.stack):
  1384 + self.stack.append(entry)
  1385 +
  1386 + # if the callstack has had an entry, it's this functions caller
  1387 + if prefix > 0:
  1388 + self.add_callee(self.stack[prefix - 1], entry)
  1389 +
  1390 + self.add_entry(entry)
  1391 +
  1392 + profile = Profile()
  1393 + profile[SAMPLES] = 0
  1394 + for _function, _callees in self.entries.itervalues():
  1395 + function = Function(_function.id, _function.name)
  1396 + function[SAMPLES] = _function.samples
  1397 + profile.add_function(function)
  1398 + profile[SAMPLES] += _function.samples
  1399 +
  1400 + if _function.image:
  1401 + function[MODULE] = os.path.basename(_function.image)
  1402 +
  1403 + for _callee in _callees.itervalues():
  1404 + call = Call(_callee.id)
  1405 + call[SAMPLES] = _callee.samples
  1406 + function.add_call(call)
  1407 +
  1408 + # compute derived data
  1409 + profile.validate()
  1410 + profile.find_cycles()
  1411 + profile.ratio(TIME_RATIO, SAMPLES)
  1412 + profile.call_ratios(SAMPLES)
  1413 + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
  1414 +
  1415 + return profile
  1416 +
  1417 +
  1418 +class SleepyParser(Parser):
  1419 + """Parser for GNU gprof output.
  1420 +
  1421 + See also:
  1422 + - http://www.codersnotes.com/sleepy/
  1423 + - http://sleepygraph.sourceforge.net/
  1424 + """
  1425 +
  1426 + def __init__(self, filename):
  1427 + Parser.__init__(self)
  1428 +
  1429 + from zipfile import ZipFile
  1430 +
  1431 + self.database = ZipFile(filename)
  1432 +
  1433 + self.symbols = {}
  1434 + self.calls = {}
  1435 +
  1436 + self.profile = Profile()
  1437 +
  1438 + _symbol_re = re.compile(
  1439 + r'^(?P<id>\w+)' +
  1440 + r'\s+"(?P<module>[^"]*)"' +
  1441 + r'\s+"(?P<procname>[^"]*)"' +
  1442 + r'\s+"(?P<sourcefile>[^"]*)"' +
  1443 + r'\s+(?P<sourceline>\d+)$'
  1444 + )
  1445 +
  1446 + def parse_symbols(self):
  1447 + lines = self.database.read('symbols.txt').splitlines()
  1448 + for line in lines:
  1449 + mo = self._symbol_re.match(line)
  1450 + if mo:
  1451 + symbol_id, module, procname, sourcefile, sourceline = mo.groups()
  1452 +
  1453 + function_id = ':'.join([module, procname])
  1454 +
  1455 + try:
  1456 + function = self.profile.functions[function_id]
  1457 + except KeyError:
  1458 + function = Function(function_id, procname)
  1459 + function[SAMPLES] = 0
  1460 + self.profile.add_function(function)
  1461 +
  1462 + self.symbols[symbol_id] = function
  1463 +
  1464 + def parse_callstacks(self):
  1465 + lines = self.database.read("callstacks.txt").splitlines()
  1466 + for line in lines:
  1467 + fields = line.split()
  1468 + samples = int(fields[0])
  1469 + callstack = fields[1:]
  1470 +
  1471 + callstack = [self.symbols[symbol_id] for symbol_id in callstack]
  1472 +
  1473 + callee = callstack[0]
  1474 +
  1475 + callee[SAMPLES] += samples
  1476 + self.profile[SAMPLES] += samples
  1477 +
  1478 + for caller in callstack[1:]:
  1479 + try:
  1480 + call = caller.calls[callee.id]
  1481 + except KeyError:
  1482 + call = Call(callee.id)
  1483 + call[SAMPLES2] = samples
  1484 + caller.add_call(call)
  1485 + else:
  1486 + call[SAMPLES2] += samples
  1487 +
  1488 + callee = caller
  1489 +
  1490 + def parse(self):
  1491 + profile = self.profile
  1492 + profile[SAMPLES] = 0
  1493 +
  1494 + self.parse_symbols()
  1495 + self.parse_callstacks()
  1496 +
  1497 + # Compute derived events
  1498 + profile.validate()
  1499 + profile.find_cycles()
  1500 + profile.ratio(TIME_RATIO, SAMPLES)
  1501 + profile.call_ratios(SAMPLES2)
  1502 + profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
  1503 +
  1504 + return profile
  1505 +
  1506 +
  1507 +class AQtimeTable:
  1508 +
  1509 + def __init__(self, name, fields):
  1510 + self.name = name
  1511 +
  1512 + self.fields = fields
  1513 + self.field_column = {}
  1514 + for column in range(len(fields)):
  1515 + self.field_column[fields[column]] = column
  1516 + self.rows = []
  1517 +
  1518 + def __len__(self):
  1519 + return len(self.rows)
  1520 +
  1521 + def __iter__(self):
  1522 + for values, children in self.rows:
  1523 + fields = {}
  1524 + for name, value in zip(self.fields, values):
  1525 + fields[name] = value
  1526 + children = dict([(child.name, child) for child in children])
  1527 + yield fields, children
  1528 + raise StopIteration
  1529 +
  1530 + def add_row(self, values, children=()):
  1531 + self.rows.append((values, children))
  1532 +
  1533 +
  1534 +class AQtimeParser(XmlParser):
  1535 +
  1536 + def __init__(self, stream):
  1537 + XmlParser.__init__(self, stream)
  1538 + self.tables = {}
  1539 +
  1540 + def parse(self):
  1541 + self.element_start('AQtime_Results')
  1542 + self.parse_headers()
  1543 + results = self.parse_results()
  1544 + self.element_end('AQtime_Results')
  1545 + return self.build_profile(results)
  1546 +
  1547 + def parse_headers(self):
  1548 + self.element_start('HEADERS')
  1549 + while self.token.type == XML_ELEMENT_START:
  1550 + self.parse_table_header()
  1551 + self.element_end('HEADERS')
  1552 +
  1553 + def parse_table_header(self):
  1554 + attrs = self.element_start('TABLE_HEADER')
  1555 + name = attrs['NAME']
  1556 + id = int(attrs['ID'])
  1557 + field_types = []
  1558 + field_names = []
  1559 + while self.token.type == XML_ELEMENT_START:
  1560 + field_type, field_name = self.parse_table_field()
  1561 + field_types.append(field_type)
  1562 + field_names.append(field_name)
  1563 + self.element_end('TABLE_HEADER')
  1564 + self.tables[id] = name, field_types, field_names
  1565 +
  1566 + def parse_table_field(self):
  1567 + attrs = self.element_start('TABLE_FIELD')
  1568 + type = attrs['TYPE']
  1569 + name = self.character_data()
  1570 + self.element_end('TABLE_FIELD')
  1571 + return type, name
  1572 +
  1573 + def parse_results(self):
  1574 + self.element_start('RESULTS')
  1575 + table = self.parse_data()
  1576 + self.element_end('RESULTS')
  1577 + return table
  1578 +
  1579 + def parse_data(self):
  1580 + rows = []
  1581 + attrs = self.element_start('DATA')
  1582 + table_id = int(attrs['TABLE_ID'])
  1583 + table_name, field_types, field_names = self.tables[table_id]
  1584 + table = AQtimeTable(table_name, field_names)
  1585 + while self.token.type == XML_ELEMENT_START:
  1586 + row, children = self.parse_row(field_types)
  1587 + table.add_row(row, children)
  1588 + self.element_end('DATA')
  1589 + return table
  1590 +
  1591 + def parse_row(self, field_types):
  1592 + row = [None]*len(field_types)
  1593 + children = []
  1594 + self.element_start('ROW')
  1595 + while self.token.type == XML_ELEMENT_START:
  1596 + if self.token.name_or_data == 'FIELD':
  1597 + field_id, field_value = self.parse_field(field_types)
  1598 + row[field_id] = field_value
  1599 + elif self.token.name_or_data == 'CHILDREN':
  1600 + children = self.parse_children()
  1601 + else:
  1602 + raise XmlTokenMismatch("<FIELD ...> or <CHILDREN ...>", self.token)
  1603 + self.element_end('ROW')
  1604 + return row, children
  1605 +
  1606 + def parse_field(self, field_types):
  1607 + attrs = self.element_start('FIELD')
  1608 + id = int(attrs['ID'])
  1609 + type = field_types[id]
  1610 + value = self.character_data()
  1611 + if type == 'Integer':
  1612 + value = int(value)
  1613 + elif type == 'Float':
  1614 + value = float(value)
  1615 + elif type == 'Address':
  1616 + value = int(value)
  1617 + elif type == 'String':
  1618 + pass
  1619 + else:
  1620 + assert False
  1621 + self.element_end('FIELD')
  1622 + return id, value
  1623 +
  1624 + def parse_children(self):
  1625 + children = []
  1626 + self.element_start('CHILDREN')
  1627 + while self.token.type == XML_ELEMENT_START:
  1628 + table = self.parse_data()
  1629 + assert table.name not in children
  1630 + children.append(table)
  1631 + self.element_end('CHILDREN')
  1632 + return children
  1633 +
  1634 + def build_profile(self, results):
  1635 + assert results.name == 'Routines'
  1636 + profile = Profile()
  1637 + profile[TIME] = 0.0
  1638 + for fields, tables in results:
  1639 + function = self.build_function(fields)
  1640 + children = tables['Children']
  1641 + for fields, _ in children:
  1642 + call = self.build_call(fields)
  1643 + function.add_call(call)
  1644 + profile.add_function(function)
  1645 + profile[TIME] = profile[TIME] + function[TIME]
  1646 + profile[TOTAL_TIME] = profile[TIME]
  1647 + profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME)
  1648 + return profile
  1649 +
  1650 + def build_function(self, fields):
  1651 + function = Function(self.build_id(fields), self.build_name(fields))
  1652 + function[TIME] = fields['Time']
  1653 + function[TOTAL_TIME] = fields['Time with Children']
  1654 + #function[TIME_RATIO] = fields['% Time']/100.0
  1655 + #function[TOTAL_TIME_RATIO] = fields['% with Children']/100.0
  1656 + return function
  1657 +
  1658 + def build_call(self, fields):
  1659 + call = Call(self.build_id(fields))
  1660 + call[TIME] = fields['Time']
  1661 + call[TOTAL_TIME] = fields['Time with Children']
  1662 + #call[TIME_RATIO] = fields['% Time']/100.0
  1663 + #call[TOTAL_TIME_RATIO] = fields['% with Children']/100.0
  1664 + return call
  1665 +
  1666 + def build_id(self, fields):
  1667 + return ':'.join([fields['Module Name'], fields['Unit Name'], fields['Routine Name']])
  1668 +
  1669 + def build_name(self, fields):
  1670 + # TODO: use more fields
  1671 + return fields['Routine Name']
  1672 +
  1673 +
  1674 +class PstatsParser:
  1675 + """Parser python profiling statistics saved with te pstats module."""
  1676 +
  1677 + def __init__(self, *filename):
  1678 + import pstats
  1679 + try:
  1680 + self.stats = pstats.Stats(*filename)
  1681 + except ValueError:
  1682 + import hotshot.stats
  1683 + self.stats = hotshot.stats.load(filename[0])
  1684 + self.profile = Profile()
  1685 + self.function_ids = {}
  1686 +
  1687 + def get_function_name(self, (filename, line, name)):
  1688 + module = os.path.splitext(filename)[0]
  1689 + module = os.path.basename(module)
  1690 + return "%s:%d:%s" % (module, line, name)
  1691 +
  1692 + def get_function(self, key):
  1693 + try:
  1694 + id = self.function_ids[key]
  1695 + except KeyError:
  1696 + id = len(self.function_ids)
  1697 + name = self.get_function_name(key)
  1698 + function = Function(id, name)
  1699 + self.profile.functions[id] = function
  1700 + self.function_ids[key] = id
  1701 + else:
  1702 + function = self.profile.functions[id]
  1703 + return function
  1704 +
  1705 + def parse(self):
  1706 + self.profile[TIME] = 0.0
  1707 + self.profile[TOTAL_TIME] = self.stats.total_tt
  1708 + for fn, (cc, nc, tt, ct, callers) in self.stats.stats.iteritems():
  1709 + callee = self.get_function(fn)
  1710 + callee[CALLS] = nc
  1711 + callee[TOTAL_TIME] = ct
  1712 + callee[TIME] = tt
  1713 + self.profile[TIME] += tt
  1714 + self.profile[TOTAL_TIME] = max(self.profile[TOTAL_TIME], ct)
  1715 + for fn, value in callers.iteritems():
  1716 + caller = self.get_function(fn)
  1717 + call = Call(callee.id)
  1718 + if isinstance(value, tuple):
  1719 + for i in xrange(0, len(value), 4):
  1720 + nc, cc, tt, ct = value[i:i+4]
  1721 + if CALLS in call:
  1722 + call[CALLS] += cc
  1723 + else:
  1724 + call[CALLS] = cc
  1725 +
  1726 + if TOTAL_TIME in call:
  1727 + call[TOTAL_TIME] += ct
  1728 + else:
  1729 + call[TOTAL_TIME] = ct
  1730 +
  1731 + else:
  1732 + call[CALLS] = value
  1733 + call[TOTAL_TIME] = ratio(value, nc)*ct
  1734 +
  1735 + caller.add_call(call)
  1736 + #self.stats.print_stats()
  1737 + #self.stats.print_callees()
  1738 +
  1739 + # Compute derived events
  1740 + self.profile.validate()
  1741 + self.profile.ratio(TIME_RATIO, TIME)
  1742 + self.profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME)
  1743 +
  1744 + return self.profile
  1745 +
  1746 +
  1747 +class Theme:
  1748 +
  1749 + def __init__(self,
  1750 + bgcolor = (0.0, 0.0, 1.0),
  1751 + mincolor = (0.0, 0.0, 0.0),
  1752 + maxcolor = (0.0, 0.0, 1.0),
  1753 + fontname = "Arial",
  1754 + minfontsize = 10.0,
  1755 + maxfontsize = 10.0,
  1756 + minpenwidth = 0.5,
  1757 + maxpenwidth = 4.0,
  1758 + gamma = 2.2,
  1759 + skew = 1.0):
  1760 + self.bgcolor = bgcolor
  1761 + self.mincolor = mincolor
  1762 + self.maxcolor = maxcolor
  1763 + self.fontname = fontname
  1764 + self.minfontsize = minfontsize
  1765 + self.maxfontsize = maxfontsize
  1766 + self.minpenwidth = minpenwidth
  1767 + self.maxpenwidth = maxpenwidth
  1768 + self.gamma = gamma
  1769 + self.skew = skew
  1770 +
  1771 + def graph_bgcolor(self):
  1772 + return self.hsl_to_rgb(*self.bgcolor)
  1773 +
  1774 + def graph_fontname(self):
  1775 + return self.fontname
  1776 +
  1777 + def graph_fontsize(self):
  1778 + return self.minfontsize
  1779 +
  1780 + def node_bgcolor(self, weight):
  1781 + return self.color(weight)
  1782 +
  1783 + def node_fgcolor(self, weight):
  1784 + return self.graph_bgcolor()
  1785 +
  1786 + def node_fontsize(self, weight):
  1787 + return self.fontsize(weight)
  1788 +
  1789 + def edge_color(self, weight):
  1790 + return self.color(weight)
  1791 +
  1792 + def edge_fontsize(self, weight):
  1793 + return self.fontsize(weight)
  1794 +
  1795 + def edge_penwidth(self, weight):
  1796 + return max(weight*self.maxpenwidth, self.minpenwidth)
  1797 +
  1798 + def edge_arrowsize(self, weight):
  1799 + return 0.5 * math.sqrt(self.edge_penwidth(weight))
  1800 +
  1801 + def fontsize(self, weight):
  1802 + return max(weight**2 * self.maxfontsize, self.minfontsize)
  1803 +
  1804 + def color(self, weight):
  1805 + weight = min(max(weight, 0.0), 1.0)
  1806 +
  1807 + hmin, smin, lmin = self.mincolor
  1808 + hmax, smax, lmax = self.maxcolor
  1809 +
  1810 + if self.skew < 0:
  1811 + raise ValueError("Skew must be greater than 0")
  1812 + elif self.skew == 1.0:
  1813 + h = hmin + weight*(hmax - hmin)
  1814 + s = smin + weight*(smax - smin)
  1815 + l = lmin + weight*(lmax - lmin)
  1816 + else:
  1817 + base = self.skew
  1818 + h = hmin + ((hmax-hmin)*(-1.0 + (base ** weight)) / (base - 1.0))
  1819 + s = smin + ((smax-smin)*(-1.0 + (base ** weight)) / (base - 1.0))
  1820 + l = lmin + ((lmax-lmin)*(-1.0 + (base ** weight)) / (base - 1.0))
  1821 +
  1822 + return self.hsl_to_rgb(h, s, l)
  1823 +
  1824 + def hsl_to_rgb(self, h, s, l):
  1825 + """Convert a color from HSL color-model to RGB.
  1826 +
  1827 + See also:
  1828 + - http://www.w3.org/TR/css3-color/#hsl-color
  1829 + """
  1830 +
  1831 + h = h % 1.0
  1832 + s = min(max(s, 0.0), 1.0)
  1833 + l = min(max(l, 0.0), 1.0)
  1834 +
  1835 + if l <= 0.5:
  1836 + m2 = l*(s + 1.0)
  1837 + else:
  1838 + m2 = l + s - l*s
  1839 + m1 = l*2.0 - m2
  1840 + r = self._hue_to_rgb(m1, m2, h + 1.0/3.0)
  1841 + g = self._hue_to_rgb(m1, m2, h)
  1842 + b = self._hue_to_rgb(m1, m2, h - 1.0/3.0)
  1843 +
  1844 + # Apply gamma correction
  1845 + r **= self.gamma
  1846 + g **= self.gamma
  1847 + b **= self.gamma
  1848 +
  1849 + return (r, g, b)
  1850 +
  1851 + def _hue_to_rgb(self, m1, m2, h):
  1852 + if h < 0.0:
  1853 + h += 1.0
  1854 + elif h > 1.0:
  1855 + h -= 1.0
  1856 + if h*6 < 1.0:
  1857 + return m1 + (m2 - m1)*h*6.0
  1858 + elif h*2 < 1.0:
  1859 + return m2
  1860 + elif h*3 < 2.0:
  1861 + return m1 + (m2 - m1)*(2.0/3.0 - h)*6.0
  1862 + else:
  1863 + return m1
  1864 +
  1865 +
  1866 +TEMPERATURE_COLORMAP = Theme(
  1867 + mincolor = (2.0/3.0, 0.80, 0.25), # dark blue
  1868 + maxcolor = (0.0, 1.0, 0.5), # satured red
  1869 + gamma = 1.0
  1870 +)
  1871 +
  1872 +PINK_COLORMAP = Theme(
  1873 + mincolor = (0.0, 1.0, 0.90), # pink
  1874 + maxcolor = (0.0, 1.0, 0.5), # satured red
  1875 +)
  1876 +
  1877 +GRAY_COLORMAP = Theme(
  1878 + mincolor = (0.0, 0.0, 0.85), # light gray
  1879 + maxcolor = (0.0, 0.0, 0.0), # black
  1880 +)
  1881 +
  1882 +BW_COLORMAP = Theme(
  1883 + minfontsize = 8.0,
  1884 + maxfontsize = 24.0,
  1885 + mincolor = (0.0, 0.0, 0.0), # black
  1886 + maxcolor = (0.0, 0.0, 0.0), # black
  1887 + minpenwidth = 0.1,
  1888 + maxpenwidth = 8.0,
  1889 +)
  1890 +
  1891 +
  1892 +class DotWriter:
  1893 + """Writer for the DOT language.
  1894 +
  1895 + See also:
  1896 + - "The DOT Language" specification
  1897 + http://www.graphviz.org/doc/info/lang.html
  1898 + """
  1899 +
  1900 + def __init__(self, fp):
  1901 + self.fp = fp
  1902 +
  1903 + def graph(self, profile, theme):
  1904 + self.begin_graph()
  1905 +
  1906 + fontname = theme.graph_fontname()
  1907 +
  1908 + self.attr('graph', fontname=fontname, ranksep=0.25, nodesep=0.125)
  1909 + self.attr('node', fontname=fontname, shape="box", style="filled", fontcolor="white", width=0, height=0)
  1910 + self.attr('edge', fontname=fontname)
  1911 +
  1912 + for function in profile.functions.itervalues():
  1913 + labels = []
  1914 + for event in PROCESS, MODULE:
  1915 + if event in function.events:
  1916 + label = event.format(function[event])
  1917 + labels.append(label)
  1918 + labels.append(function.name)
  1919 + for event in TOTAL_TIME_RATIO, TIME_RATIO, CALLS:
  1920 + if event in function.events:
  1921 + label = event.format(function[event])
  1922 + labels.append(label)
  1923 +
  1924 + try:
  1925 + weight = function[PRUNE_RATIO]
  1926 + except UndefinedEvent:
  1927 + weight = 0.0
  1928 +
  1929 + label = '\n'.join(labels)
  1930 + self.node(function.id,
  1931 + label = label,
  1932 + color = self.color(theme.node_bgcolor(weight)),
  1933 + fontcolor = self.color(theme.node_fgcolor(weight)),
  1934 + fontsize = "%.2f" % theme.node_fontsize(weight),
  1935 + )
  1936 +
  1937 + for call in function.calls.itervalues():
  1938 + callee = profile.functions[call.callee_id]
  1939 +
  1940 + labels = []
  1941 + for event in TOTAL_TIME_RATIO, CALLS:
  1942 + if event in call.events:
  1943 + label = event.format(call[event])
  1944 + labels.append(label)
  1945 +
  1946 + try:
  1947 + weight = call[PRUNE_RATIO]
  1948 + except UndefinedEvent:
  1949 + try:
  1950 + weight = callee[PRUNE_RATIO]
  1951 + except UndefinedEvent:
  1952 + weight = 0.0
  1953 +
  1954 + label = '\n'.join(labels)
  1955 +
  1956 + self.edge(function.id, call.callee_id,
  1957 + label = label,
  1958 + color = self.color(theme.edge_color(weight)),
  1959 + fontcolor = self.color(theme.edge_color(weight)),
  1960 + fontsize = "%.2f" % theme.edge_fontsize(weight),
  1961 + penwidth = "%.2f" % theme.edge_penwidth(weight),
  1962 + labeldistance = "%.2f" % theme.edge_penwidth(weight),
  1963 + arrowsize = "%.2f" % theme.edge_arrowsize(weight),
  1964 + )
  1965 +
  1966 + self.end_graph()
  1967 +
  1968 + def begin_graph(self):
  1969 + self.write('digraph {\n')
  1970 +
  1971 + def end_graph(self):
  1972 + self.write('}\n')
  1973 +
  1974 + def attr(self, what, **attrs):
  1975 + self.write("\t")
  1976 + self.write(what)
  1977 + self.attr_list(attrs)
  1978 + self.write(";\n")
  1979 +
  1980 + def node(self, node, **attrs):
  1981 + self.write("\t")
  1982 + self.id(node)
  1983 + self.attr_list(attrs)
  1984 + self.write(";\n")
  1985 +
  1986 + def edge(self, src, dst, **attrs):
  1987 + self.write("\t")
  1988 + self.id(src)
  1989 + self.write(" -> ")
  1990 + self.id(dst)
  1991 + self.attr_list(attrs)
  1992 + self.write(";\n")
  1993 +
  1994 + def attr_list(self, attrs):
  1995 + if not attrs:
  1996 + return
  1997 + self.write(' [')
  1998 + first = True
  1999 + for name, value in attrs.iteritems():
  2000 + if first:
  2001 + first = False
  2002 + else:
  2003 + self.write(", ")
  2004 + self.id(name)
  2005 + self.write('=')
  2006 + self.id(value)
  2007 + self.write(']')
  2008 +
  2009 + def id(self, id):
  2010 + if isinstance(id, (int, float)):
  2011 + s = str(id)
  2012 + elif isinstance(id, basestring):
  2013 + if id.isalnum():
  2014 + s = id
  2015 + else:
  2016 + s = self.escape(id)
  2017 + else:
  2018 + raise TypeError
  2019 + self.write(s)
  2020 +
  2021 + def color(self, (r, g, b)):
  2022 +
  2023 + def float2int(f):
  2024 + if f <= 0.0:
  2025 + return 0
  2026 + if f >= 1.0:
  2027 + return 255
  2028 + return int(255.0*f + 0.5)
  2029 +
  2030 + return "#" + "".join(["%02x" % float2int(c) for c in (r, g, b)])
  2031 +
  2032 + def escape(self, s):
  2033 + s = s.encode('utf-8')
  2034 + s = s.replace('\\', r'\\')
  2035 + s = s.replace('\n', r'\n')
  2036 + s = s.replace('\t', r'\t')
  2037 + s = s.replace('"', r'\"')
  2038 + return '"' + s + '"'
  2039 +
  2040 + def write(self, s):
  2041 + self.fp.write(s)
  2042 +
  2043 +
  2044 +class Main:
  2045 + """Main program."""
  2046 +
  2047 + themes = {
  2048 + "color": TEMPERATURE_COLORMAP,
  2049 + "pink": PINK_COLORMAP,
  2050 + "gray": GRAY_COLORMAP,
  2051 + "bw": BW_COLORMAP,
  2052 + }
  2053 +
  2054 + def main(self):
  2055 + """Main program."""
  2056 +
  2057 + parser = optparse.OptionParser(
  2058 + usage="\n\t%prog [options] [file] ...",
  2059 + version="%%prog %s" % __version__)
  2060 + parser.add_option(
  2061 + '-o', '--output', metavar='FILE',
  2062 + type="string", dest="output",
  2063 + help="output filename [stdout]")
  2064 + parser.add_option(
  2065 + '-n', '--node-thres', metavar='PERCENTAGE',
  2066 + type="float", dest="node_thres", default=0.5,
  2067 + help="eliminate nodes below this threshold [default: %default]")
  2068 + parser.add_option(
  2069 + '-e', '--edge-thres', metavar='PERCENTAGE',
  2070 + type="float", dest="edge_thres", default=0.1,
  2071 + help="eliminate edges below this threshold [default: %default]")
  2072 + parser.add_option(
  2073 + '-f', '--format',
  2074 + type="choice", choices=('prof', 'oprofile', 'sysprof', 'pstats', 'shark', 'sleepy', 'aqtime'),
  2075 + dest="format", default="prof",
  2076 + help="profile format: prof, oprofile, sysprof, shark, sleepy, aqtime, or pstats [default: %default]")
  2077 + parser.add_option(
  2078 + '-c', '--colormap',
  2079 + type="choice", choices=('color', 'pink', 'gray', 'bw'),
  2080 + dest="theme", default="color",
  2081 + help="color map: color, pink, gray, or bw [default: %default]")
  2082 + parser.add_option(
  2083 + '-s', '--strip',
  2084 + action="store_true",
  2085 + dest="strip", default=False,
  2086 + help="strip function parameters, template parameters, and const modifiers from demangled C++ function names")
  2087 + parser.add_option(
  2088 + '-w', '--wrap',
  2089 + action="store_true",
  2090 + dest="wrap", default=False,
  2091 + help="wrap function names")
  2092 + # add a new option to control skew of the colorization curve
  2093 + parser.add_option(
  2094 + '--skew',
  2095 + type="float", dest="theme_skew", default=1.0,
  2096 + help="skew the colorization curve. Values < 1.0 give more variety to lower percentages. Value > 1.0 give less variety to lower percentages")
  2097 + (self.options, self.args) = parser.parse_args(sys.argv[1:])
  2098 +
  2099 + if len(self.args) > 1 and self.options.format != 'pstats':
  2100 + parser.error('incorrect number of arguments')
  2101 +
  2102 + try:
  2103 + self.theme = self.themes[self.options.theme]
  2104 + except KeyError:
  2105 + parser.error('invalid colormap \'%s\'' % self.options.theme)
  2106 +
  2107 + # set skew on the theme now that it has been picked.
  2108 + if self.options.theme_skew:
  2109 + self.theme.skew = self.options.theme_skew
  2110 +
  2111 + if self.options.format == 'prof':
  2112 + if not self.args:
  2113 + fp = sys.stdin
  2114 + else:
  2115 + fp = open(self.args[0], 'rt')
  2116 + parser = GprofParser(fp)
  2117 + elif self.options.format == 'oprofile':
  2118 + if not self.args:
  2119 + fp = sys.stdin
  2120 + else:
  2121 + fp = open(self.args[0], 'rt')
  2122 + parser = OprofileParser(fp)
  2123 + elif self.options.format == 'sysprof':
  2124 + if not self.args:
  2125 + fp = sys.stdin
  2126 + else:
  2127 + fp = open(self.args[0], 'rt')
  2128 + parser = SysprofParser(fp)
  2129 + elif self.options.format == 'pstats':
  2130 + if not self.args:
  2131 + parser.error('at least a file must be specified for pstats input')
  2132 + parser = PstatsParser(*self.args)
  2133 + elif self.options.format == 'shark':
  2134 + if not self.args:
  2135 + fp = sys.stdin
  2136 + else:
  2137 + fp = open(self.args[0], 'rt')
  2138 + parser = SharkParser(fp)
  2139 + elif self.options.format == 'sleepy':
  2140 + if len(self.args) != 1:
  2141 + parser.error('exactly one file must be specified for sleepy input')
  2142 + parser = SleepyParser(self.args[0])
  2143 + elif self.options.format == 'aqtime':
  2144 + if not self.args:
  2145 + fp = sys.stdin
  2146 + else:
  2147 + fp = open(self.args[0], 'rt')
  2148 + parser = AQtimeParser(fp)
  2149 + else:
  2150 + parser.error('invalid format \'%s\'' % self.options.format)
  2151 +
  2152 + self.profile = parser.parse()
  2153 +
  2154 + if self.options.output is None:
  2155 + self.output = sys.stdout
  2156 + else:
  2157 + self.output = open(self.options.output, 'wt')
  2158 +
  2159 + self.write_graph()
  2160 +
  2161 + _parenthesis_re = re.compile(r'\([^()]*\)')
  2162 + _angles_re = re.compile(r'<[^<>]*>')
  2163 + _const_re = re.compile(r'\s+const$')
  2164 +
  2165 + def strip_function_name(self, name):
  2166 + """Remove extraneous information from C++ demangled function names."""
  2167 +
  2168 + # Strip function parameters from name by recursively removing paired parenthesis
  2169 + while True:
  2170 + name, n = self._parenthesis_re.subn('', name)
  2171 + if not n:
  2172 + break
  2173 +
  2174 + # Strip const qualifier
  2175 + name = self._const_re.sub('', name)
  2176 +
  2177 + # Strip template parameters from name by recursively removing paired angles
  2178 + while True:
  2179 + name, n = self._angles_re.subn('', name)
  2180 + if not n:
  2181 + break
  2182 +
  2183 + return name
  2184 +
  2185 + def wrap_function_name(self, name):
  2186 + """Split the function name on multiple lines."""
  2187 +
  2188 + if len(name) > 32:
  2189 + ratio = 2.0/3.0
  2190 + height = max(int(len(name)/(1.0 - ratio) + 0.5), 1)
  2191 + width = max(len(name)/height, 32)
  2192 + # TODO: break lines in symbols
  2193 + name = textwrap.fill(name, width, break_long_words=False)
  2194 +
  2195 + # Take away spaces
  2196 + name = name.replace(", ", ",")
  2197 + name = name.replace("> >", ">>")
  2198 + name = name.replace("> >", ">>") # catch consecutive
  2199 +
  2200 + return name
  2201 +
  2202 + def compress_function_name(self, name):
  2203 + """Compress function name according to the user preferences."""
  2204 +
  2205 + if self.options.strip:
  2206 + name = self.strip_function_name(name)
  2207 +
  2208 + if self.options.wrap:
  2209 + name = self.wrap_function_name(name)
  2210 +
  2211 + # TODO: merge functions with same resulting name
  2212 +
  2213 + return name
  2214 +
  2215 + def write_graph(self):
  2216 + dot = DotWriter(self.output)
  2217 + profile = self.profile
  2218 + profile.prune(self.options.node_thres/100.0, self.options.edge_thres/100.0)
  2219 +
  2220 + for function in profile.functions.itervalues():
  2221 + function.name = self.compress_function_name(function.name)
  2222 +
  2223 + dot.graph(profile, self.theme)
  2224 +
  2225 +
  2226 +if __name__ == '__main__':
  2227 + Main().main()
This file is too large to display.
  1 +gprof图形化输出工具: gprof2dot.py graphviz-2.18.tar.gz build_gprof2dot.sh
  2 +
  3 +dot:
  4 + http://www.graphviz.org/
  5 + http://www.graphviz.org/Download_source.php
  6 + graphviz-2.18.tar.gz 绘图工具
  7 + build_gprof2dot.sh 编译graphviz,命令为dot。
  8 + 要求是sudoer,需要sudo make install。
  9 +
  10 +gprof2dot.py:
  11 + 将gprof的日志绘图。
  12 +
  13 +使用方法:
  14 +0. 若需要图形化,编译dot:
  15 + cd 3rdparty/gprof && bash build_gprof2dot.sh
  16 +1. srs配置时:
  17 + ./configure --with-gprof
  18 + 脚本会加入编译参数"-pg -lc_p",gcc -g -pg -lc_p -c xxx -o xxx.o,即在configure中打开 Performance="-pg -lc_p"
  19 + 链接时,加入链接选项"-pg",否则无法工作:gcc -pg -o srs xxxx.o,即在configure中打开 PerformanceLink="-pg"
  20 +2. 编译和启动程序:make && ./objs/srs -c conf/srs.conf
  21 + 退出程序,按CTRL+C,可以看到生成了gmon.out,这个就是性能的统计数据。
  22 +3. gprof生成报表:
  23 + gprof -b ./objs/srs gmon.out > gprof.srs.log
  24 +4. 将报表生成图片:
  25 + ./3rdparty/gprof/gprof2dot.py gprof.srs.log | dot -Tpng -o ~/winlin.png
  26 +
  27 +缩写语句:
  28 + # 生成 ~/winlin.log ~/winlin.png
  29 + rm -f gmon.out; ./objs/srs -c conf/srs.conf
  30 + # 用户按CTRL+C
  31 + file="winlin";gprof -b ./objs/srs gmon.out > ~/${file}.log; ./3rdparty/gprof/gprof2dot.py ~/${file}.log | dot -Tpng -o ~/${file}.png
  32 +
  33 +备注:
  34 + 其实gprof生成的日志就可以看,不一定要图形化。
  35 + 也就是dot和gprof2dot都不用执行。
  36 + 参考:http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
  1 +Only in .: 1.st.arm.patch
  2 +diff -r -c ./md.h ../st-1.9-patch-arm/md.h
  3 +*** ./md.h 2009-10-02 02:46:43.000000000 +0800
  4 +--- ../st-1.9-patch-arm/md.h 2014-03-16 20:49:03.845344804 +0800
  5 +***************
  6 +*** 422,428 ****
  7 + #define MD_STACK_GROWS_DOWN
  8 +
  9 + #if defined(__GLIBC__) && __GLIBC__ >= 2
  10 +! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[20]
  11 + #else
  12 + #error "ARM/Linux pre-glibc2 not supported yet"
  13 + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
  14 +--- 422,428 ----
  15 + #define MD_STACK_GROWS_DOWN
  16 +
  17 + #if defined(__GLIBC__) && __GLIBC__ >= 2
  18 +! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8]
  19 + #else
  20 + #error "ARM/Linux pre-glibc2 not supported yet"
  21 + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
  1 +http-parser-2.1.zip
  2 + for srs to support http callback.
  3 +
  4 +nginx-1.5.7.zip
  5 + for srs to support hls streaming.
  6 +
  7 +st-1.9.zip
  8 + basic framework for srs.
  9 +
  10 +openssl-1.0.1f.zip
  11 + openssl for SRS(with-ssl) RTMP complex handshake to delivery h264+aac stream.
  12 +
  13 +CherryPy-3.2.4.zip
  14 + sample api server for srs.
  15 +
  16 +ffmpeg-2.1.1.tar.gz
  17 +yasm-1.2.0.tar.gz
  18 +lame-3.99.5.tar.gz
  19 +libaacplus-2.0.2.tar.gz
  20 +libaacplus-patch-26410-800.zip (26410-800.zip)
  21 +speex-1.2rc1.zip
  22 +x264-snapshot-20131129-2245-stable.tar.bz2 (core.138)
  23 + for srs to support live stream transcoding.
  24 + remark: we use *.zip for all linux plantform.
  25 +
  26 +tools/ccache-3.1.9.zip
  27 + to fast build.
  28 +
  29 +1.st.arm.Makefile.patch
  30 + st编译脚本补丁,允许用户指定cc编译器。
  31 +
  32 +gtest-1.6.0.zip
  33 + google单元测试框架。
  34 +
  35 +gperftools-2.1.zip
  36 + google性能分析和测试工具。
  37 + 编译和使用参考压缩文件中的README和doc目录。
  38 +
  39 +links:
  40 + nginx:
  41 + http://nginx.org/
  42 + http-parser:
  43 + https://github.com/joyent/http-parser
  44 + state-threads:
  45 + http://sourceforge.net/projects/state-threads
  46 + ffmpeg:
  47 + http://ffmpeg.org/
  48 + http://ffmpeg.org/releases/ffmpeg-2.1.1.tar.gz
  49 + x264:
  50 + http://www.videolan.org/
  51 + ftp://ftp.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20131129-2245-stable.tar.bz2
  52 + lame:
  53 + http://sourceforge.net/projects/lame/
  54 + http://nchc.dl.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz
  55 + aacplus:
  56 + http://217.20.164.161/~tipok/aacplus/
  57 + http://217.20.164.161/~tipok/aacplus/libaacplus-2.0.2.tar.gz
  58 + aacplus-patch:
  59 + http://www.3gpp.org/DynaReport/26410.htm
  60 + http://www.3gpp.org/ftp/Specs/archive/26_series/26.410/26410-800.zip
  61 + yasm:
  62 + http://yasm.tortall.net/
  63 + http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz
  64 + cherrypy:
  65 + http://www.cherrypy.org/
  66 + https://pypi.python.org/pypi/CherryPy/3.2.4
  67 + openssl:
  68 + http://www.openssl.org/
  69 + http://www.openssl.org/source/openssl-1.0.1f.tar.gz
  70 + gtest:
  71 + https://code.google.com/p/googletest
  72 + https://code.google.com/p/googletest/downloads/list
  73 + gperftools:
  74 + https://code.google.com/p/gperftools/
  75 + https://code.google.com/p/gperftools/downloads/list
  76 + speex:
  77 + http://www.speex.org/downloads/
  78 + http://downloads.xiph.org/releases/speex/speex-1.2rc1.tar.gz
  79 +
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
  1 +cmake_minimum_required(VERSION 2.6.4)
  2 +project(srs CXX)
  3 +
  4 +INCLUDE_DIRECTORIES(objs objs/st objs/hp objs/openssl src/core src/kernel src/rtmp src/app)
  5 +
  6 +set(SOURCE_FILES src/main/srs_main_server.cpp)
  7 +AUX_SOURCE_DIRECTORY(src/core SOURCE_FILES)
  8 +AUX_SOURCE_DIRECTORY(src/kernel SOURCE_FILES)
  9 +AUX_SOURCE_DIRECTORY(src/rtmp SOURCE_FILES)
  10 +AUX_SOURCE_DIRECTORY(src/app SOURCE_FILES)
  11 +
  12 +ADD_EXECUTABLE(srs ${SOURCE_FILES})
  13 +TARGET_LINK_LIBRARIES(srs dl)
  14 +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
  15 +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libssl.a)
  16 +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libcrypto.a)
  17 +TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/hp/libhttp_parser.a)
  18 +
  19 +IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
  20 + MESSAGE("srs_libs not found")
  21 + EXEC_PROGRAM(./configure)
  22 +ENDIF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
  23 +
  24 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  25 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  26 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  27 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  28 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  29 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  30 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  31 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  32 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  33 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  34 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  35 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  36 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  37 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  38 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  39 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  40 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  41 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  42 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  43 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  44 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  45 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  46 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  47 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  48 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  49 +MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
  50 +MESSAGE(STATUS "use ./configure && make, @see https://github.com/winlinvip/simple-rtmp-server#usage")
  51 +
  1 +# generate the binary
  2 +#
  3 +# params:
  4 +# $SRS_OBJS the objs directory. ie. objs
  5 +# $SRS_MAKEFILE the makefile name. ie. Makefile
  6 +#
  7 +# $MAIN_ENTRANCES array, disable all except the $APP_MAIN itself. ie. ["srs_main_server" "srs_main_bandcheck"]
  8 +# $APP_MAIN the object file that contains main function. ie. srs_main_server
  9 +# $BUILD_KEY a string indicates the build key for Makefile. ie. srs
  10 +# $APP_NAME the app name to output. ie. srs
  11 +# $MODULE_OBJS array, the objects to compile the app.
  12 +# $ModuleLibFiles array, the 3rdpart library file to link with. ie. [objs/st-1.9/obj/libst.a objs/libx264/obj/libx264.a]
  13 +# $LINK_OPTIONS the linker options. ie. -ldl
  14 +
  15 +FILE=${SRS_OBJS}/${SRS_MAKEFILE}
  16 +
  17 +APP_TARGET="${SRS_OBJS}/${APP_NAME}"
  18 +
  19 +echo "generate app ${APP_NAME} depends...";
  20 +
  21 +echo "# build ${APP_TARGET}" >> ${FILE}
  22 +# generate the binary depends, for example:
  23 +# srs: objs/srs
  24 +echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE}
  25 +# the link commands, for example:
  26 +# objs/srs: objs/src/core/srs_core.o
  27 +echo -n "${APP_TARGET}: " >> ${FILE}
  28 +for item in ${MODULE_OBJS[*]}; do
  29 + FILE_NAME=`basename $item`
  30 + FILE_NAME=${FILE_NAME%.*}
  31 +
  32 + ignored=0
  33 + for disabled_item in ${MAIN_ENTRANCES[*]}; do
  34 + if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then
  35 + ignored=1
  36 + continue;
  37 + fi
  38 + done
  39 +
  40 + if [ ! -f ${item} ]; then
  41 + ignored=1
  42 + fi
  43 +
  44 + if [ ${ignored} == 1 ]; then
  45 + continue;
  46 + fi
  47 +
  48 + OBJ_FILE=${SRS_OBJS}/$item
  49 + OBJ_FILE="${OBJ_FILE%.*}.o"
  50 + echo -n "${OBJ_FILE} " >> ${FILE}
  51 +done
  52 +echo "" >> ${FILE}
  53 +
  54 +echo "generate app ${APP_NAME} link...";
  55 +
  56 +# genereate the actual link command, for example:
  57 +# $(LINK) -o objs/srs objs/src/core/srs_core.o -ldl
  58 +echo -n " \$(LINK) -o ${APP_TARGET} " >> ${FILE}
  59 +for item in ${MODULE_OBJS[*]}; do
  60 + FILE_NAME=`basename $item`
  61 + FILE_NAME=${FILE_NAME%.*}
  62 +
  63 + ignored=0
  64 + for disabled_item in ${MAIN_ENTRANCES[*]}; do
  65 + if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then
  66 + ignored=1
  67 + continue;
  68 + fi
  69 + done
  70 +
  71 + if [ ! -f ${item} ]; then
  72 + ignored=1
  73 + fi
  74 +
  75 + if [ ${ignored} == 1 ]; then
  76 + continue;
  77 + fi
  78 +
  79 + OBJ_FILE=${SRS_OBJS}/$item
  80 + OBJ_FILE="${OBJ_FILE%.*}.o"
  81 + echo -n "${OBJ_FILE} " >> ${FILE}
  82 +done
  83 +# 3rdpart library static link.
  84 +for item in ${ModuleLibFiles[*]}; do
  85 + echo -n "$item " >> ${FILE}
  86 +done
  87 +# link options.
  88 +echo -n "${LINK_OPTIONS}" >> ${FILE}
  89 +echo "" >> ${FILE}
  90 +
  91 +echo -n "generate app ${APP_NAME} ok"; echo '!';
  1 +#!/bin/bash
  2 +
  3 +ff_src_dir="../../3rdparty"
  4 +
  5 +# the jobs to make ffmpeg
  6 +if [[ "" -eq SRS_JOBS ]]; then
  7 + export SRS_JOBS="--jobs=1"
  8 +fi
  9 +
  10 +ff_current_dir=$(pwd -P)
  11 +ff_build_dir="${ff_current_dir}/_build"
  12 +ff_release_dir="${ff_current_dir}/_release"
  13 +echo "start to build the tools for transcode system:"
  14 +echo "current_dir: ${ff_current_dir}"
  15 +echo "build_dir: ${ff_build_dir}"
  16 +echo "release_dir: ${ff_release_dir}"
  17 +echo "SRS_JOBS: ${SRS_JOBS}"
  18 +
  19 +mkdir -p ${ff_build_dir}
  20 +mkdir -p ${ff_release_dir}
  21 +
  22 +# yasm for libx264
  23 +ff_yasm_bin=${ff_release_dir}/bin/yasm
  24 +if [[ -f ${ff_yasm_bin} ]]; then
  25 + echo "yasm is ok"
  26 +else
  27 + echo "build yasm-1.2.0"
  28 + cd $ff_current_dir &&
  29 + rm -rf yasm-1.2.0 && unzip -q ${ff_src_dir}/yasm-1.2.0.zip &&
  30 + cd yasm-1.2.0 && ./configure --prefix=${ff_release_dir} &&
  31 + make && make install
  32 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build yasm-1.2.0 failed"; exit 1; fi
  33 +fi
  34 +# add yasm to path, for x264 to use yasm directly.
  35 +# ffmpeg can specifies the yasm path when configure it.
  36 +export PATH=${PATH}:${ff_release_dir}/bin
  37 +
  38 +# libaacplus
  39 +if [[ -f ${ff_release_dir}/lib/libaacplus.a ]]; then
  40 + echo "libaacplus is ok"
  41 +else
  42 + echo "build yasm-1.2.0"
  43 + cd $ff_current_dir &&
  44 + rm -rf libaacplus-2.0.2 && unzip -q ${ff_src_dir}/libaacplus-2.0.2.zip &&
  45 + cd libaacplus-2.0.2 && cp ../${ff_src_dir}/libaacplus-patch-26410-800.zip src/26410-800.zip &&
  46 + bash autogen.sh && ./configure --prefix=${ff_release_dir} --enable-static && make && make install
  47 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build libaacplus-2.0.2 failed"; exit 1; fi
  48 +fi
  49 +
  50 +# lame-3.99
  51 +if [[ -f ${ff_release_dir}/lib/libmp3lame.a ]]; then
  52 + echo "libmp3lame is ok"
  53 +else
  54 + echo "build lame-3.99.5"
  55 + cd $ff_current_dir &&
  56 + rm -rf lame-3.99.5 && unzip -q ${ff_src_dir}/lame-3.99.5.zip &&
  57 + cd lame-3.99.5 && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install
  58 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build lame-3.99.5 failed"; exit 1; fi
  59 +fi
  60 +
  61 +# speex-1.2rc1
  62 +if [[ -f ${ff_release_dir}/lib/libspeex.a ]]; then
  63 + echo "libspeex is ok"
  64 +else
  65 + echo "build speex-1.2rc1"
  66 + cd $ff_current_dir &&
  67 + rm -rf speex-1.2rc1 && unzip -q ${ff_src_dir}/speex-1.2rc1.zip &&
  68 + cd speex-1.2rc1 && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install
  69 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build speex-1.2rc1 failed"; exit 1; fi
  70 +fi
  71 +
  72 +# x264 core.138
  73 +if [[ -f ${ff_release_dir}/lib/libx264.a ]]; then
  74 + echo "x264 is ok"
  75 +else
  76 + echo "build x264"
  77 + cd $ff_current_dir &&
  78 + rm -rf x264-snapshot-20131129-2245-stable && unzip -q ${ff_src_dir}/x264-snapshot-20131129-2245-stable.zip &&
  79 + cd x264-snapshot-20131129-2245-stable &&
  80 + ./configure --prefix=${ff_release_dir} --disable-opencl --bit-depth=8 \
  81 + --enable-static --disable-avs --disable-swscale --disable-lavf \
  82 + --disable-ffms --disable-gpac &&
  83 + make ${SRS_JOBS} && make install
  84 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build x264 failed"; exit 1; fi
  85 +fi
  86 +
  87 +# ffmpeg-2.1.1
  88 +if [[ -f ${ff_release_dir}/bin/ffmpeg ]]; then
  89 + echo "ffmpeg-2.1.1 is ok"
  90 +else
  91 + echo "build ffmpeg-2.1.1"
  92 + cd $ff_current_dir &&
  93 + rm -rf ffmpeg-2.1.1 && unzip -q ${ff_src_dir}/ffmpeg-2.1.1.zip &&
  94 + echo "remove all so to force the ffmpeg to build in static" &&
  95 + rm -f ${ff_release_dir}/lib/*.so* &&
  96 + echo "export the dir to enable the build command canbe use." &&
  97 + export ffmpeg_exported_release_dir=${ff_release_dir} &&
  98 + cd ffmpeg-2.1.1 &&
  99 + ./configure \
  100 + --enable-gpl --enable-nonfree \
  101 + --yasmexe=${ff_yasm_bin} \
  102 + --prefix=${ff_release_dir} --cc= \
  103 + --enable-static --disable-shared --disable-debug \
  104 + --extra-cflags='-I${ffmpeg_exported_release_dir}/include' \
  105 + --extra-ldflags='-L${ffmpeg_exported_release_dir}/lib -lm -ldl' \
  106 + --disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc \
  107 + --enable-postproc --enable-bzlib --enable-zlib --enable-parsers \
  108 + --enable-libx264 --enable-libmp3lame --enable-libaacplus --enable-libspeex \
  109 + --enable-pthreads --extra-libs=-lpthread \
  110 + --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers &&
  111 + make ${SRS_JOBS} && make install
  112 + ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build ffmpeg failed"; exit 1; fi
  113 +fi
  1 +#!/bin/bash
  2 +
  3 +# variables, parent script must set it:
  4 +# SRS_JOBS: the build jobs.
  5 +# SrsArmMakeOptions: the arm make options for ubuntu12(armhf, v7cpu)
  6 +# SRS_AUTO_HEADERS_H: the auto generated header file.
  7 +
  8 +#####################################################################################
  9 +#####################################################################################
  10 +# prepare the depends tools and libraries
  11 +# DEPENDS: options.sh, only when user options parsed, the depends tools are known.
  12 +#####################################################################################
  13 +#####################################################################################
  14 +
  15 +#####################################################################################
  16 +# utilities
  17 +#####################################################################################
  18 +function require_sudoer()
  19 +{
  20 + sudo echo "" >/dev/null 2>&1
  21 +
  22 + ret=$?; if [[ 0 -ne $ret ]]; then
  23 + echo "\"$1\" require sudoer failed. ret=$ret";
  24 + exit $ret;
  25 + fi
  26 +}
  27 +
  28 +# TODO: check gcc/g++
  29 +echo "check gcc/g++/gdb/make"
  30 +echo "depends tools are ok"
  31 +#####################################################################################
  32 +# for Ubuntu, auto install tools by apt-get
  33 +#####################################################################################
  34 +OS_IS_UBUNTU=NO
  35 +function Ubuntu_prepare()
  36 +{
  37 + if [ $SRS_CUBIE = YES ]; then
  38 + echo "for cubieboard, use ubuntu prepare"
  39 + else
  40 + uname -v|grep Ubuntu >/dev/null 2>&1
  41 + ret=$?; if [[ 0 -ne $ret ]]; then
  42 + return 0;
  43 + fi
  44 + fi
  45 +
  46 + OS_IS_UBUNTU=YES
  47 + echo "Ubuntu detected, install tools if needed"
  48 +
  49 + gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  50 + echo "install gcc"
  51 + require_sudoer "sudo apt-get install -y --force-yes gcc"
  52 + sudo apt-get install -y --force-yes gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  53 + echo "install gcc success"
  54 + fi
  55 +
  56 + g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  57 + echo "install g++"
  58 + require_sudoer "sudo apt-get install -y --force-yes g++"
  59 + sudo apt-get install -y --force-yes g++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  60 + echo "install g++ success"
  61 + fi
  62 +
  63 + make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  64 + echo "install make"
  65 + require_sudoer "sudo apt-get install -y --force-yes make"
  66 + sudo apt-get install -y --force-yes make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  67 + echo "install make success"
  68 + fi
  69 +
  70 + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  71 + echo "install patch"
  72 + require_sudoer "sudo apt-get install -y --force-yes patch"
  73 + sudo apt-get install -y --force-yes patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  74 + echo "install patch success"
  75 + fi
  76 +
  77 + if [ $SRS_FFMPEG_TOOL = YES ]; then
  78 + autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  79 + echo "install autoconf"
  80 + require_sudoer "sudo apt-get install -y --force-yes autoconf"
  81 + sudo apt-get install -y --force-yes autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  82 + echo "install autoconf success"
  83 + fi
  84 +
  85 + libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  86 + echo "install libtool"
  87 + require_sudoer "sudo apt-get install -y --force-yes libtool"
  88 + sudo apt-get install -y --force-yes libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  89 + echo "install libtool success"
  90 + fi
  91 +
  92 + if [[ ! -f /usr/include/pcre.h ]]; then
  93 + echo "install libpcre3-dev"
  94 + require_sudoer "sudo apt-get install -y --force-yes libpcre3-dev"
  95 + sudo apt-get install -y --force-yes libpcre3-dev; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  96 + echo "install libpcre3-dev success"
  97 + fi
  98 +
  99 + if [[ ! -f /usr/include/zlib.h ]]; then
  100 + echo "install zlib1g-dev"
  101 + require_sudoer "sudo apt-get install -y --force-yes zlib1g-dev"
  102 + sudo apt-get install -y --force-yes zlib1g-dev; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  103 + echo "install zlib1g-dev success"
  104 + fi
  105 + fi
  106 +
  107 + # for arm, install the cross build tool chain.
  108 + if [ $SRS_ARM_UBUNTU12 = YES ]; then
  109 + $SrsArmCC --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  110 + echo "install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi"
  111 + require_sudoer "sudo apt-get install -y --force-yes gcc-arm-linux-gnueabi g++-arm-linux-gnueabi"
  112 + sudo apt-get install -y --force-yes gcc-arm-linux-gnueabi g++-arm-linux-gnueabi; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  113 + echo "install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi success"
  114 + fi
  115 + fi
  116 +
  117 + # for mips, user must installed the tool chain.
  118 + if [ $SRS_MIPS_UBUNTU12 = YES ]; then
  119 + $SrsArmCC --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  120 + echo "user must install the tool chain: $SrsArmCC"
  121 + return 2
  122 + fi
  123 + fi
  124 +
  125 + echo "Ubuntu install tools success"
  126 + return 0
  127 +}
  128 +Ubuntu_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "Ubuntu prepare failed, ret=$ret"; exit $ret; fi
  129 +#####################################################################################
  130 +# for Centos, auto install tools by yum
  131 +#####################################################################################
  132 +OS_IS_CENTOS=NO
  133 +function Centos_prepare()
  134 +{
  135 + if [[ ! -f /etc/redhat-release ]]; then
  136 + return 0;
  137 + fi
  138 +
  139 + OS_IS_CENTOS=YES
  140 + echo "Centos detected, install tools if needed"
  141 +
  142 + gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  143 + echo "install gcc"
  144 + require_sudoer "sudo yum install -y gcc"
  145 + sudo yum install -y gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  146 + echo "install gcc success"
  147 + fi
  148 +
  149 + g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  150 + echo "install gcc-c++"
  151 + require_sudoer "sudo yum install -y gcc-c++"
  152 + sudo yum install -y gcc-c++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  153 + echo "install gcc-c++ success"
  154 + fi
  155 +
  156 + make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  157 + echo "install make"
  158 + require_sudoer "sudo yum install -y make"
  159 + sudo yum install -y make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  160 + echo "install make success"
  161 + fi
  162 +
  163 + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  164 + echo "install patch"
  165 + require_sudoer "sudo yum install -y patch"
  166 + sudo yum install -y patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  167 + echo "install patch success"
  168 + fi
  169 +
  170 + if [ $SRS_FFMPEG_TOOL = YES ]; then
  171 + automake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  172 + echo "install automake"
  173 + require_sudoer "sudo yum install -y automake"
  174 + sudo yum install -y automake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  175 + echo "install automake success"
  176 + fi
  177 +
  178 + autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  179 + echo "install autoconf"
  180 + require_sudoer "sudo yum install -y autoconf"
  181 + sudo yum install -y autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  182 + echo "install autoconf success"
  183 + fi
  184 +
  185 + libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  186 + echo "install libtool"
  187 + require_sudoer "sudo yum install -y libtool"
  188 + sudo yum install -y libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  189 + echo "install libtool success"
  190 + fi
  191 +
  192 + if [[ ! -f /usr/include/pcre.h ]]; then
  193 + echo "install pcre-devel"
  194 + require_sudoer "sudo yum install -y pcre-devel"
  195 + sudo yum install -y pcre-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  196 + echo "install pcre-devel success"
  197 + fi
  198 +
  199 + if [[ ! -f /usr/include/zlib.h ]]; then
  200 + echo "install zlib-devel"
  201 + require_sudoer "sudo yum install -y zlib-devel"
  202 + sudo yum install -y zlib-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  203 + echo "install zlib-devel success"
  204 + fi
  205 + fi
  206 +
  207 + # for arm, install the cross build tool chain.
  208 + if [ $SRS_EMBEDED_CPU = YES ]; then
  209 + echo "embeded(arm/mips) is invalid for CentOS"
  210 + return 1
  211 + fi
  212 +
  213 + echo "Centos install tools success"
  214 + return 0
  215 +}
  216 +Centos_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "CentOS prepare failed, ret=$ret"; exit $ret; fi
  217 +#####################################################################################
  218 +# for OSX, auto install tools by brew
  219 +#####################################################################################
  220 +OS_IS_OSX=NO
  221 +function OSX_prepare()
  222 +{
  223 + SYS_NAME=`uname -s`
  224 + if [ $SYS_NAME != Darwin ]; then
  225 + echo "This is not Darwin OSX"
  226 + return 0;
  227 + fi
  228 +
  229 + OS_IS_OSX=YES
  230 + echo "OSX detected, install tools if needed"
  231 +
  232 + gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  233 + echo "install gcc"
  234 + require_sudoer "sudo brew install gcc"
  235 + sudo brew install gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  236 + echo "install gcc success"
  237 + fi
  238 +
  239 + g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  240 + echo "install gcc-c++"
  241 + require_sudoer "sudo brew install gcc-c++"
  242 + sudo brew install gcc-c++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  243 + echo "install gcc-c++ success"
  244 + fi
  245 +
  246 + make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  247 + echo "install make"
  248 + require_sudoer "sudo brew install make"
  249 + sudo brew install make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  250 + echo "install make success"
  251 + fi
  252 +
  253 + patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  254 + echo "install patch"
  255 + require_sudoer "sudo brew install patch"
  256 + sudo brew install patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  257 + echo "install patch success"
  258 + fi
  259 +
  260 + if [ $SRS_FFMPEG_TOOL = YES ]; then
  261 + automake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  262 + echo "install automake"
  263 + require_sudoer "sudo brew install automake"
  264 + sudo brew install automake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  265 + echo "install automake success"
  266 + fi
  267 +
  268 + autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  269 + echo "install autoconf"
  270 + require_sudoer "sudo brew install autoconf"
  271 + sudo brew install autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  272 + echo "install autoconf success"
  273 + fi
  274 +
  275 + libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
  276 + echo "install libtool"
  277 + require_sudoer "sudo brew install libtool"
  278 + sudo brew install libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  279 + echo "install libtool success"
  280 + fi
  281 +
  282 + if [[ ! -f /usr/include/pcre.h ]]; then
  283 + echo "install pcre-devel"
  284 + require_sudoer "sudo brew install pcre-devel"
  285 + sudo brew install pcre-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  286 + echo "install pcre-devel success"
  287 + fi
  288 +
  289 + if [[ ! -f /usr/include/zlib.h ]]; then
  290 + echo "install zlib-devel"
  291 + require_sudoer "sudo brew install zlib-devel"
  292 + sudo brew install zlib-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
  293 + echo "install zlib-devel success"
  294 + fi
  295 + fi
  296 +
  297 + echo "OSX install tools success"
  298 + return 0
  299 +}
  300 +OSX_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "OSX prepare failed, ret=$ret"; exit $ret; fi
  301 +
  302 +
  303 +#####################################################################################
  304 +# st-1.9
  305 +#####################################################################################
  306 +# check the arm flag file, if flag changed, need to rebuild the st.
  307 +_ST_MAKE=linux-debug
  308 +if [ $SRS_EMBEDED_CPU = YES ]; then
  309 + # ok, arm specified, if the flag filed does not exists, need to rebuild.
  310 + if [[ -f ${SRS_OBJS}/_flag.st.arm.tmp && -f ${SRS_OBJS}/st/libst.a ]]; then
  311 + echo "st-1.9t for arm is ok.";
  312 + else
  313 + # TODO: FIXME: patch the bug.
  314 + # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm#st-arm-bug-fix
  315 + echo "build st-1.9t for arm";
  316 + (
  317 + rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} &&
  318 + unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
  319 + patch -p0 < ../../3rdparty/patches/1.st.arm.patch &&
  320 + make CC=${SrsArmCC} AR=${SrsArmAR} LD=${SrsArmLD} RANDLIB=${SrsArmRANDLIB} EXTRA_CFLAGS="-DMD_HAVE_EPOLL" ${_ST_MAKE} &&
  321 + cd .. && rm -rf st && ln -sf st-1.9/obj st &&
  322 + cd .. && touch ${SRS_OBJS}/_flag.st.arm.tmp
  323 + )
  324 + fi
  325 +else
  326 + if [ $SRS_OSX = YES ]; then
  327 + _ST_MAKE=darwin-debug
  328 + fi
  329 + if [[ ! -f ${SRS_OBJS}/_flag.st.arm.tmp && -f ${SRS_OBJS}/st/libst.a ]]; then
  330 + echo "st-1.9t is ok.";
  331 + else
  332 + echo "build st-1.9t";
  333 + (
  334 + rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} &&
  335 + unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
  336 + echo "we alaways patch the st, for we may build srs under arm directly" &&
  337 + echo "the 1.st.arm.patch is ok for x86 because it's only modify code under macro linux arm" &&
  338 + patch -p0 < ../../3rdparty/patches/1.st.arm.patch &&
  339 + make ${_ST_MAKE} &&
  340 + cd .. && rm -rf st && ln -sf st-1.9/obj st &&
  341 + cd .. && rm -f ${SRS_OBJS}/_flag.st.arm.tmp
  342 + )
  343 + fi
  344 +fi
  345 +# check status
  346 +ret=$?; if [[ $ret -ne 0 ]]; then echo "build st-1.9 failed, ret=$ret"; exit $ret; fi
  347 +if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "build st-1.9 static lib failed."; exit -1; fi
  348 +
  349 +#####################################################################################
  350 +# http-parser-2.1
  351 +#####################################################################################
  352 +# check the arm flag file, if flag changed, need to rebuild the st.
  353 +if [ $SRS_HTTP_PARSER = YES ]; then
  354 + # for osx(darwin), donot use sed.
  355 + if [ $SRS_OSX = YES ]; then
  356 + if [[ -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then
  357 + echo "http-parser-2.1 is ok.";
  358 + else
  359 + echo "build http-parser-2.1 for osx(darwin)";
  360 + (
  361 + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip &&
  362 + cd http-parser-2.1 &&
  363 + make package &&
  364 + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp
  365 + )
  366 + fi
  367 + # ok, arm specified, if the flag filed does not exists, need to rebuild.
  368 + elif [ $SRS_EMBEDED_CPU = YES ]; then
  369 + if [[ -f ${SRS_OBJS}/_flag.st.hp.tmp && -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then
  370 + echo "http-parser-2.1 for arm is ok.";
  371 + else
  372 + echo "build http-parser-2.1 for arm";
  373 + (
  374 + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip &&
  375 + cd http-parser-2.1 &&
  376 + sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile &&
  377 + sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile &&
  378 + make CC=${SrsArmCC} AR=${SrsArmAR} package &&
  379 + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp &&
  380 + cd .. && touch ${SRS_OBJS}/_flag.st.hp.tmp
  381 + )
  382 + fi
  383 + else
  384 + # arm not specified, if exists flag, need to rebuild for no-arm platform.
  385 + if [[ ! -f ${SRS_OBJS}/_flag.st.hp.tmp && -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then
  386 + echo "http-parser-2.1 is ok.";
  387 + else
  388 + echo "build http-parser-2.1";
  389 + (
  390 + rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip &&
  391 + cd http-parser-2.1 &&
  392 + sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile &&
  393 + sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile &&
  394 + make package &&
  395 + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp &&
  396 + cd .. && rm -f ${SRS_OBJS}/_flag.st.hp.tmp
  397 + )
  398 + fi
  399 + fi
  400 +
  401 + # check status
  402 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build http-parser-2.1 failed, ret=$ret"; exit $ret; fi
  403 + if [[ ! -f ${SRS_OBJS}/hp/http_parser.h ]]; then echo "build http-parser-2.1 failed"; exit -1; fi
  404 + if [[ ! -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then echo "build http-parser-2.1 failed"; exit -1; fi
  405 +fi
  406 +
  407 +if [ $SRS_HTTP_PARSER = YES ]; then
  408 + echo "#define SRS_AUTO_HTTP_PARSER" >> $SRS_AUTO_HEADERS_H
  409 +else
  410 + echo "#undef SRS_AUTO_HTTP_PARSER" >> $SRS_AUTO_HEADERS_H
  411 +fi
  412 +
  413 +if [ $SRS_HTTP_SERVER = YES ]; then
  414 + echo "#define SRS_AUTO_HTTP_SERVER" >> $SRS_AUTO_HEADERS_H
  415 +else
  416 + echo "#undef SRS_AUTO_HTTP_SERVER" >> $SRS_AUTO_HEADERS_H
  417 +fi
  418 +
  419 +if [ $SRS_HTTP_API = YES ]; then
  420 + echo "#define SRS_AUTO_HTTP_API" >> $SRS_AUTO_HEADERS_H
  421 +else
  422 + echo "#undef SRS_AUTO_HTTP_API" >> $SRS_AUTO_HEADERS_H
  423 +fi
  424 +
  425 +#####################################################################################
  426 +# nginx for HLS, nginx-1.5.0
  427 +#####################################################################################
  428 +function write_nginx_html5()
  429 +{
  430 + cat<<END > ${html_file}
  431 +<video width="640" height="360"
  432 + autoplay controls autobuffer
  433 + src="${hls_stream}"
  434 + type="application/vnd.apple.mpegurl">
  435 +</video>
  436 +END
  437 +}
  438 +# create the nginx dir, for http-server if not build nginx
  439 +mkdir -p ${SRS_OBJS}/nginx
  440 +# make nginx
  441 +__SRS_BUILD_NGINX=NO; if [ $SRS_EMBEDED_CPU = NO ]; then if [ $SRS_NGINX = YES ]; then __SRS_BUILD_NGINX=YES; fi fi
  442 +if [ $__SRS_BUILD_NGINX = YES ]; then
  443 + if [[ -f ${SRS_OBJS}/nginx/sbin/nginx ]]; then
  444 + echo "nginx-1.5.7 is ok.";
  445 + else
  446 + echo "build nginx-1.5.7";
  447 + (
  448 + rm -rf ${SRS_OBJS}/nginx-1.5.7 && cd ${SRS_OBJS} &&
  449 + unzip -q ../3rdparty/nginx-1.5.7.zip && cd nginx-1.5.7 &&
  450 + ./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install &&
  451 + cd .. && rm -rf nginx && ln -sf nginx-1.5.7/_release nginx
  452 + )
  453 + fi
  454 + # check status
  455 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build nginx-1.5.7 failed, ret=$ret"; exit $ret; fi
  456 + if [ ! -f ${SRS_OBJS}/nginx/sbin/nginx ]; then echo "build nginx-1.5.7 failed."; exit -1; fi
  457 +
  458 + # use current user to config nginx,
  459 + # srs will write ts/m3u8 file use current user,
  460 + # nginx default use nobody, so cannot read the ts/m3u8 created by srs.
  461 + cp ${SRS_OBJS}/nginx/conf/nginx.conf ${SRS_OBJS}/nginx/conf/nginx.conf.bk
  462 + sed -i "s/^.user nobody;/user `whoami`;/g" ${SRS_OBJS}/nginx/conf/nginx.conf
  463 +fi
  464 +
  465 +# create forward dir
  466 +mkdir -p ${SRS_OBJS}/nginx/html/live &&
  467 +mkdir -p ${SRS_OBJS}/nginx/html/forward/live
  468 +
  469 +# generate default html pages for android.
  470 +html_file=${SRS_OBJS}/nginx/html/live/demo.html && hls_stream=demo.m3u8 && write_nginx_html5
  471 +html_file=${SRS_OBJS}/nginx/html/live/livestream.html && hls_stream=livestream.m3u8 && write_nginx_html5
  472 +html_file=${SRS_OBJS}/nginx/html/live/livestream_ld.html && hls_stream=livestream_ld.m3u8 && write_nginx_html5
  473 +html_file=${SRS_OBJS}/nginx/html/live/livestream_sd.html && hls_stream=livestream_sd.m3u8 && write_nginx_html5
  474 +html_file=${SRS_OBJS}/nginx/html/forward/live/livestream.html && hls_stream=livestream.m3u8 && write_nginx_html5
  475 +html_file=${SRS_OBJS}/nginx/html/forward/live/livestream_ld.html && hls_stream=livestream_ld.m3u8 && write_nginx_html5
  476 +html_file=${SRS_OBJS}/nginx/html/forward/live/livestream_sd.html && hls_stream=livestream_sd.m3u8 && write_nginx_html5
  477 +
  478 +# copy players to nginx html dir.
  479 +rm -rf ${SRS_OBJS}/nginx/html/players &&
  480 +ln -sf `pwd`/research/players ${SRS_OBJS}/nginx/html/players &&
  481 +rm -f ${SRS_OBJS}/nginx/crossdomain.xml &&
  482 +ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml
  483 +
  484 +# for favicon.ico
  485 +rm -rf ${SRS_OBJS}/nginx/html/favicon.ico &&
  486 +ln -sf `pwd`/research/api-server/static-dir/favicon.ico ${SRS_OBJS}/nginx/html/favicon.ico
  487 +
  488 +# nginx.html to detect whether nginx is alive
  489 +echo "nginx is ok" > ${SRS_OBJS}/nginx/html/nginx.html
  490 +
  491 +if [ $SRS_NGINX = YES ]; then
  492 + echo "#define SRS_AUTO_NGINX" >> $SRS_AUTO_HEADERS_H
  493 +else
  494 + echo "#undef SRS_AUTO_NGINX" >> $SRS_AUTO_HEADERS_H
  495 +fi
  496 +
  497 +if [ $SRS_DVR = YES ]; then
  498 + echo "#define SRS_AUTO_DVR" >> $SRS_AUTO_HEADERS_H
  499 +else
  500 + echo "#undef SRS_AUTO_DVR" >> $SRS_AUTO_HEADERS_H
  501 +fi
  502 +
  503 +if [ $SRS_HLS = YES ]; then
  504 + echo "#define SRS_AUTO_HLS" >> $SRS_AUTO_HEADERS_H
  505 +else
  506 + echo "#undef SRS_AUTO_HLS" >> $SRS_AUTO_HEADERS_H
  507 +fi
  508 +
  509 +#####################################################################################
  510 +# cherrypy for http hooks callback, CherryPy-3.2.4
  511 +#####################################################################################
  512 +if [ $SRS_HTTP_CALLBACK = YES ]; then
  513 + if [[ -f ${SRS_OBJS}/CherryPy-3.2.4/setup.py ]]; then
  514 + echo "CherryPy-3.2.4 is ok.";
  515 + else
  516 + require_sudoer "configure --with-http-callback"
  517 + echo "install CherryPy-3.2.4";
  518 + (
  519 + sudo rm -rf ${SRS_OBJS}/CherryPy-3.2.4 && cd ${SRS_OBJS} &&
  520 + unzip -q ../3rdparty/CherryPy-3.2.4.zip && cd CherryPy-3.2.4 &&
  521 + sudo python setup.py install
  522 + )
  523 + fi
  524 + # check status
  525 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build CherryPy-3.2.4 failed, ret=$ret"; exit $ret; fi
  526 + if [ ! -f ${SRS_OBJS}/CherryPy-3.2.4/setup.py ]; then echo "build CherryPy-3.2.4 failed."; exit -1; fi
  527 +fi
  528 +
  529 +if [ $SRS_HTTP_CALLBACK = YES ]; then
  530 + echo "#define SRS_AUTO_HTTP_CALLBACK" >> $SRS_AUTO_HEADERS_H
  531 +else
  532 + echo "#undef SRS_AUTO_HTTP_CALLBACK" >> $SRS_AUTO_HEADERS_H
  533 +fi
  534 +
  535 +echo "link players to cherrypy static-dir"
  536 +rm -rf research/api-server/static-dir/players &&
  537 +ln -sf `pwd`/research/players research/api-server/static-dir/players &&
  538 +rm -f research/api-server/static-dir/crossdomain.xml &&
  539 +ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml &&
  540 +rm -rf research/api-server/static-dir/live &&
  541 +mkdir -p `pwd`/${SRS_OBJS}/nginx/html/live &&
  542 +ln -sf `pwd`/${SRS_OBJS}/nginx/html/live research/api-server/static-dir/live &&
  543 +rm -rf research/api-server/static-dir/forward &&
  544 +mkdir -p `pwd`/${SRS_OBJS}/nginx/html/forward &&
  545 +ln -sf `pwd`/${SRS_OBJS}/nginx/html/forward research/api-server/static-dir/forward
  546 +ret=$?; if [[ $ret -ne 0 ]]; then echo "link players to cherrypy static-dir failed, ret=$ret"; exit $ret; fi
  547 +
  548 +#####################################################################################
  549 +# generate demo index.html
  550 +#####################################################################################
  551 +# if nginx enalbed, generate nginx index file.
  552 +if [ $__SRS_BUILD_NGINX = YES ]; then
  553 + rm -f ${SRS_OBJS}/nginx/html/index.html &&
  554 + ln -sf `pwd`/research/players/nginx_index.html ${SRS_OBJS}/nginx/html/index.html
  555 +fi
  556 +# if http-server enalbed, use srs embeded http-server
  557 +if [ $SRS_HTTP_SERVER = YES ]; then
  558 + rm -f ${SRS_OBJS}/nginx/html/index.html &&
  559 + ln -sf `pwd`/research/players/srs-http-server_index.html ${SRS_OBJS}/nginx/html/index.html
  560 +fi
  561 +# if api-server enabled, generate for api server.
  562 +if [ $SRS_HTTP_CALLBACK = YES ]; then
  563 + rm -f ${SRS_OBJS}/nginx/html/index.html &&
  564 + ln -sf `pwd`/research/players/api-server_index.html ${SRS_OBJS}/nginx/html/index.html
  565 +fi
  566 +
  567 +#####################################################################################
  568 +# openssl, for rtmp complex handshake
  569 +#####################################################################################
  570 +# extra configure options
  571 +CONFIGURE_TOOL="./config"
  572 +EXTRA_CONFIGURE=""
  573 +if [ $SRS_OSX = YES ]; then
  574 + CONFIGURE_TOOL="./Configure"
  575 + arch=`uname -m` && echo "OSX $arch";
  576 + if [ $arch = x86_64 ]; then
  577 + echo "configure 64bit openssl";
  578 + EXTRA_CONFIGURE=darwin64-x86_64-cc
  579 + else
  580 + echo "configure 32bit openssl";
  581 + EXTRA_CONFIGURE=darwin-i386-cc
  582 + fi
  583 + echo "openssl extra config: $CONFIGURE_TOOL $EXTRA_CONFIGURE"
  584 +fi
  585 +if [ $SRS_EMBEDED_CPU = YES ]; then
  586 + CONFIGURE_TOOL="./Configure"
  587 +fi
  588 +# @see http://www.openssl.org/news/secadv_20140407.txt
  589 +# Affected users should upgrade to OpenSSL 1.0.1g. Users unable to immediately
  590 +# upgrade can alternatively recompile OpenSSL with -DOPENSSL_NO_HEARTBEATS.
  591 +if [ $SRS_SSL = YES ]; then
  592 + if [ $SRS_USE_SYS_SSL = YES ]; then
  593 + echo "warning: donot compile ssl, use system ssl"
  594 + else
  595 + # check the arm flag file, if flag changed, need to rebuild the st.
  596 + if [ $SRS_EMBEDED_CPU = YES ]; then
  597 + # ok, arm specified, if the flag filed does not exists, need to rebuild.
  598 + if [[ -f ${SRS_OBJS}/_flag.ssl.arm.tmp && -f ${SRS_OBJS}/openssl/lib/libssl.a ]]; then
  599 + echo "openssl-1.0.1f for arm is ok.";
  600 + else
  601 + echo "build openssl-1.0.1f for arm";
  602 + (
  603 + rm -rf ${SRS_OBJS}/openssl-1.0.1f && cd ${SRS_OBJS} &&
  604 + unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
  605 + $CONFIGURE_TOOL --prefix=`pwd`/_release -no-shared no-asm linux-armv4 -DOPENSSL_NO_HEARTBEATS ${EXTRA_CONFIGURE} &&
  606 + make CC=${SrsArmCC} GCC=${SrsArmGCC} AR="${SrsArmAR} r" \
  607 + LD=${SrsArmLD} LINK=${SrsArmGCC} RANDLIB=${SrsArmRANDLIB} &&
  608 + make install_sw &&
  609 + cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
  610 + cd .. && touch ${SRS_OBJS}/_flag.ssl.arm.tmp
  611 + )
  612 + fi
  613 + else
  614 + # arm not specified, if exists flag, need to rebuild for no-arm platform.
  615 + if [[ ! -f ${SRS_OBJS}/_flag.ssl.arm.tmp && -f ${SRS_OBJS}/openssl/lib/libssl.a ]]; then
  616 + echo "openssl-1.0.1f is ok.";
  617 + else
  618 + echo "build openssl-1.0.1f";
  619 + (
  620 + rm -rf ${SRS_OBJS}/openssl-1.0.1f && cd ${SRS_OBJS} &&
  621 + unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
  622 + $CONFIGURE_TOOL --prefix=`pwd`/_release -no-shared -DOPENSSL_NO_HEARTBEATS ${EXTRA_CONFIGURE} &&
  623 + make && make install_sw &&
  624 + cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
  625 + cd .. && rm -f ${SRS_OBJS}/_flag.ssl.arm.tmp
  626 + )
  627 + fi
  628 + fi
  629 + # check status
  630 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build openssl-1.0.1f failed, ret=$ret"; exit $ret; fi
  631 + if [ ! -f ${SRS_OBJS}/openssl/lib/libssl.a ]; then echo "build openssl-1.0.1f failed."; exit -1; fi
  632 + fi
  633 +fi
  634 +
  635 +if [ $SRS_SSL = YES ]; then
  636 + echo "#define SRS_AUTO_SSL" >> $SRS_AUTO_HEADERS_H
  637 +else
  638 + echo "#undef SRS_AUTO_SSL" >> $SRS_AUTO_HEADERS_H
  639 +fi
  640 +
  641 +#####################################################################################
  642 +# live transcoding, ffmpeg-2.1, x264-core138, lame-3.99.5, libaacplus-2.0.2.
  643 +#####################################################################################
  644 +if [ $SRS_FFMPEG_TOOL = YES ]; then
  645 + if [[ -f ${SRS_OBJS}/ffmpeg/bin/ffmpeg ]]; then
  646 + echo "ffmpeg-2.1 is ok.";
  647 + else
  648 + echo "build ffmpeg-2.1";
  649 + (
  650 + cd ${SRS_OBJS} && pwd_dir=`pwd` &&
  651 + rm -rf ffmepg.src && mkdir -p ffmpeg.src && cd ffmpeg.src &&
  652 + rm -f build_ffmpeg.sh && ln -sf ../../auto/build_ffmpeg.sh && . build_ffmpeg.sh &&
  653 + cd ${pwd_dir} && rm -rf ffmpeg && ln -sf ffmpeg.src/_release ffmpeg
  654 + )
  655 + fi
  656 + # check status
  657 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build ffmpeg-2.1 failed, ret=$ret"; exit $ret; fi
  658 + if [ ! -f ${SRS_OBJS}/ffmpeg/bin/ffmpeg ]; then echo "build ffmpeg-2.1 failed."; exit -1; fi
  659 +fi
  660 +
  661 +# whether compile ffmpeg tool
  662 +if [ $SRS_FFMPEG_TOOL = YES ]; then
  663 + echo "#define SRS_AUTO_FFMPEG_TOOL" >> $SRS_AUTO_HEADERS_H
  664 +else
  665 + echo "#undef SRS_AUTO_FFMPEG_TOOL" >> $SRS_AUTO_HEADERS_H
  666 +fi
  667 +
  668 +# whatever the FFMPEG tools, if transcode and ingest specified,
  669 +# srs always compile the FFMPEG tool stub which used to start the FFMPEG process.
  670 +if [ $SRS_FFMPEG_STUB = YES ]; then
  671 + echo "#define SRS_AUTO_FFMPEG_STUB" >> $SRS_AUTO_HEADERS_H
  672 +else
  673 + echo "#undef SRS_AUTO_FFMPEG_STUB" >> $SRS_AUTO_HEADERS_H
  674 +fi
  675 +
  676 +if [ $SRS_TRANSCODE = YES ]; then
  677 + echo "#define SRS_AUTO_TRANSCODE" >> $SRS_AUTO_HEADERS_H
  678 +else
  679 + echo "#undef SRS_AUTO_TRANSCODE" >> $SRS_AUTO_HEADERS_H
  680 +fi
  681 +
  682 +if [ $SRS_INGEST = YES ]; then
  683 + echo "#define SRS_AUTO_INGEST" >> $SRS_AUTO_HEADERS_H
  684 +else
  685 + echo "#undef SRS_AUTO_INGEST" >> $SRS_AUTO_HEADERS_H
  686 +fi
  687 +
  688 +# for statistic.
  689 +if [ $SRS_STAT = YES ]; then
  690 + echo "#define SRS_AUTO_STAT" >> $SRS_AUTO_HEADERS_H
  691 +else
  692 + echo "#undef SRS_AUTO_STAT" >> $SRS_AUTO_HEADERS_H
  693 +fi
  694 +
  695 +#####################################################################################
  696 +# build research code, librtmp
  697 +#####################################################################################
  698 +if [ $SRS_RESEARCH = YES ]; then
  699 + mkdir -p ${SRS_OBJS}/research
  700 +
  701 + (cd research/hls && make ${SRS_JOBS} && mv ts_info ../../${SRS_OBJS}/research)
  702 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/hls failed, ret=$ret"; exit $ret; fi
  703 +
  704 + (cd research/ffempty && make ${SRS_JOBS} && mv ffempty ../../${SRS_OBJS}/research)
  705 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/ffempty failed, ret=$ret"; exit $ret; fi
  706 +fi
  707 +
  708 +if [ $SRS_LIBRTMP = YES ]; then
  709 + mkdir -p ${SRS_OBJS}/research
  710 +
  711 + # librtmp
  712 + (cd research/librtmp && mkdir -p objs && ln -sf `pwd`/objs ../../${SRS_OBJS}/research/librtmp)
  713 + ret=$?; if [[ $ret -ne 0 ]]; then echo "link research/librtmp failed, ret=$ret"; exit $ret; fi
  714 +fi
  715 +
  716 +#####################################################################################
  717 +# build utest code
  718 +#####################################################################################
  719 +if [ $SRS_UTEST = YES ]; then
  720 + if [[ -f ${SRS_OBJS}/gtest/include/gtest/gtest.h ]]; then
  721 + echo "gtest-1.6.0 is ok.";
  722 + else
  723 + echo "build gtest-1.6.0";
  724 + (
  725 + rm -rf ${SRS_OBJS}/gtest-1.6.0 && cd ${SRS_OBJS} &&
  726 + unzip -q ../3rdparty/gtest-1.6.0.zip &&
  727 + rm -rf gtest && ln -sf gtest-1.6.0 gtest
  728 + )
  729 + fi
  730 + # check status
  731 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build gtest-1.6.0 failed, ret=$ret"; exit $ret; fi
  732 + if [ ! -f ${SRS_OBJS}/gtest/include/gtest/gtest.h ]; then echo "build gtest-1.6.0 failed."; exit -1; fi
  733 +fi
  734 +
  735 +#####################################################################################
  736 +# build gperf code
  737 +#####################################################################################
  738 +if [ $SRS_GPERF = YES ]; then
  739 + if [[ -f ${SRS_OBJS}/gperf/bin/pprof ]]; then
  740 + echo "gperftools-2.1 is ok.";
  741 + else
  742 + echo "build gperftools-2.1";
  743 + (
  744 + rm -rf ${SRS_OBJS}/gperftools-2.1 && cd ${SRS_OBJS} &&
  745 + unzip -q ../3rdparty/gperftools-2.1.zip && cd gperftools-2.1 &&
  746 + ./configure --prefix=`pwd`/_release --enable-frame-pointers && make ${SRS_JOBS} && make install &&
  747 + cd .. && rm -rf gperf && ln -sf gperftools-2.1/_release gperf &&
  748 + rm -rf pprof && ln -sf gperf/bin/pprof pprof
  749 + )
  750 + fi
  751 + # check status
  752 + ret=$?; if [[ $ret -ne 0 ]]; then echo "build gperftools-2.1 failed, ret=$ret"; exit $ret; fi
  753 + if [ ! -f ${SRS_OBJS}/gperf/bin/pprof ]; then echo "build gperftools-2.1 failed."; exit -1; fi
  754 +fi
  755 +
  756 +if [ $SRS_GPERF = YES ]; then
  757 + echo "#define SRS_AUTO_GPERF" >> $SRS_AUTO_HEADERS_H
  758 +else
  759 + echo "#undef SRS_AUTO_GPERF" >> $SRS_AUTO_HEADERS_H
  760 +fi
  761 +if [ $SRS_GPERF_MC = YES ]; then
  762 + echo "#define SRS_AUTO_GPERF_MC" >> $SRS_AUTO_HEADERS_H
  763 +else
  764 + echo "#undef SRS_AUTO_GPERF_MC" >> $SRS_AUTO_HEADERS_H
  765 +fi
  766 +if [ $SRS_GPERF_MP = YES ]; then
  767 + echo "#define SRS_AUTO_GPERF_MP" >> $SRS_AUTO_HEADERS_H
  768 +else
  769 + echo "#undef SRS_AUTO_GPERF_MP" >> $SRS_AUTO_HEADERS_H
  770 +fi
  771 +if [ $SRS_GPERF_CP = YES ]; then
  772 + echo "#define SRS_AUTO_GPERF_CP" >> $SRS_AUTO_HEADERS_H
  773 +else
  774 + echo "#undef SRS_AUTO_GPERF_CP" >> $SRS_AUTO_HEADERS_H
  775 +fi
  776 +
  777 +#####################################################################################
  778 +# for embeded.
  779 +#####################################################################################
  780 +if [ $SRS_EMBEDED_CPU = YES ]; then
  781 + echo "#define SRS_AUTO_EMBEDED_CPU" >> $SRS_AUTO_HEADERS_H
  782 +else
  783 + echo "#undef SRS_AUTO_EMBEDED_CPU" >> $SRS_AUTO_HEADERS_H
  784 +fi
  785 +
  786 +# arm
  787 +if [ $SRS_ARM_UBUNTU12 = YES ]; then
  788 + echo "#define SRS_AUTO_ARM_UBUNTU12" >> $SRS_AUTO_HEADERS_H
  789 +else
  790 + echo "#undef SRS_AUTO_ARM_UBUNTU12" >> $SRS_AUTO_HEADERS_H
  791 +fi
  792 +
  793 +# mips
  794 +if [ $SRS_MIPS_UBUNTU12 = YES ]; then
  795 + echo "#define SRS_AUTO_MIPS_UBUNTU12" >> $SRS_AUTO_HEADERS_H
  796 +else
  797 + echo "#undef SRS_AUTO_MIPS_UBUNTU12" >> $SRS_AUTO_HEADERS_H
  798 +fi
  799 +
  800 +echo "" >> $SRS_AUTO_HEADERS_H
  801 +
  802 +# for log level compile settings
  803 +if [ $SRS_LOG_VERBOSE = YES ]; then
  804 + echo "#define SRS_AUTO_VERBOSE" >> $SRS_AUTO_HEADERS_H
  805 +else
  806 + echo "#undef SRS_AUTO_VERBOSE" >> $SRS_AUTO_HEADERS_H
  807 +fi
  808 +if [ $SRS_LOG_INFO = YES ]; then
  809 + echo "#define SRS_AUTO_INFO" >> $SRS_AUTO_HEADERS_H
  810 +else
  811 + echo "#undef SRS_AUTO_INFO" >> $SRS_AUTO_HEADERS_H
  812 +fi
  813 +if [ $SRS_LOG_TRACE = YES ]; then
  814 + echo "#define SRS_AUTO_TRACE" >> $SRS_AUTO_HEADERS_H
  815 +else
  816 + echo "#undef SRS_AUTO_TRACE" >> $SRS_AUTO_HEADERS_H
  817 +fi
  818 +
  819 +# prefix
  820 +echo "" >> $SRS_AUTO_HEADERS_H
  821 +echo "#define SRS_AUTO_PREFIX \"${SRS_PREFIX}\"" >> $SRS_AUTO_HEADERS_H
  822 +
  823 +echo "" >> $SRS_AUTO_HEADERS_H
  824 +
  825 +#####################################################################################
  826 +# generated the contributors from AUTHORS.txt
  827 +#####################################################################################
  828 +SRS_CONSTRIBUTORS=`cat ../AUTHORS.txt|grep "*"|awk '{print $2}'`
  829 +echo "#define SRS_AUTO_CONSTRIBUTORS \"\\" >> $SRS_AUTO_HEADERS_H
  830 +for CONTRIBUTOR in $SRS_CONSTRIBUTORS; do
  831 + echo "${CONTRIBUTOR} \\" >> $SRS_AUTO_HEADERS_H
  832 +done
  833 +echo "\"" >> $SRS_AUTO_HEADERS_H
  834 +
  835 +# new empty line to auto headers file.
  836 +echo "" >> $SRS_AUTO_HEADERS_H
  837 +
  838 +#####################################################################################
  839 +# generated the test script
  840 +#####################################################################################
  841 +rm -rf ${SRS_OBJS}/srs.test && ln -sf `pwd`/scripts/srs.test objs/srs.test
  1 +#!/bin/bash
  2 +# genereate the library header file.
  3 +
  4 +objs=$1
  5 +
  6 +rm -f $objs/include/srs_librtmp.h &&
  7 +cp $objs/../src/libs/srs_librtmp.hpp $objs/include/srs_librtmp.h
  8 +echo "genereate srs-librtmp headers success"
  1 +# generate the library for static link.
  2 +#
  3 +# params:
  4 +# $SRS_OBJS the objs directory. ie. objs
  5 +# $SRS_MAKEFILE the makefile name. ie. Makefile
  6 +#
  7 +# $BUILD_KEY a string indicates the build key for Makefile. ie. dump
  8 +# $LIB_NAME the app name to output. ie. smart_server
  9 +# $MODULE_OBJS array, the objects to compile the app.
  10 +
  11 +FILE=${SRS_OBJS}/${SRS_MAKEFILE}
  12 +
  13 +LIB_TARGET="${SRS_OBJS}/${LIB_NAME}"
  14 +LIB_TAGET_STATIC="${LIB_TARGET}.a"
  15 +
  16 +echo "generate lib ${LIB_NAME} depends..."
  17 +
  18 +echo "" >> ${FILE}
  19 +echo "# archive library ${LIB_TAGET_STATIC}" >> ${FILE}
  20 +echo "${BUILD_KEY}: ${LIB_TAGET_STATIC}" >> ${FILE}
  21 +
  22 +# build depends
  23 +echo -n "${LIB_TAGET_STATIC}: " >> ${FILE}
  24 +for item in ${MODULE_OBJS[*]}; do
  25 + FILE_NAME=`basename $item`
  26 + FILE_NAME=${FILE_NAME%.*}
  27 +
  28 + if [ ! -f ${item} ]; then
  29 + continue;
  30 + fi
  31 +
  32 + OBJ_FILE=${SRS_OBJS}/$item
  33 + OBJ_FILE="${OBJ_FILE%.*}.o"
  34 + echo -n "${OBJ_FILE} " >> ${FILE}
  35 +done
  36 +echo "" >> ${FILE}
  37 +
  38 +# build header file
  39 +echo -n " @bash auto/generate_header.sh ${SRS_OBJS}" >> ${FILE}
  40 +echo "" >> ${FILE}
  41 +
  42 +# archive librtmp.a
  43 +echo -n " \$(AR) -rs ${LIB_TAGET_STATIC} " >> ${FILE}
  44 +for item in ${MODULE_OBJS[*]}; do
  45 + FILE_NAME=`basename $item`
  46 + FILE_NAME=${FILE_NAME%.*}
  47 +
  48 + if [ ! -f ${item} ]; then
  49 + continue;
  50 + fi
  51 +
  52 + OBJ_FILE=${SRS_OBJS}/$item
  53 + OBJ_FILE="${OBJ_FILE%.*}.o"
  54 + echo -n "${OBJ_FILE} " >> ${FILE}
  55 +done
  56 +echo "" >> ${FILE}
  57 +
  58 +# parent Makefile, to create module output dir before compile it.
  59 +echo " mkdir -p ${SRS_OBJS}/include" >> ${SRS_MAKEFILE}
  60 +echo " mkdir -p ${SRS_OBJS}/lib" >> ${SRS_MAKEFILE}
  61 +
  62 +echo -n "generate lib ${LIB_NAME} ok"; echo '!';
  1 +ip=`ifconfig 2>&1|grep 'inet addr'|grep -v '127.0.0.1'|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'`
  2 +if [[ -z $ip ]]; then
  3 + echo "127.0.0.1"
  4 +else
  5 + echo $ip
  6 +fi
  1 +# generate the module info to Makefile
  2 +#
  3 +# params:
  4 +# $SRS_OBJS the objs directory. ie. objs
  5 +# $SRS_MAKEFILE the makefile name. ie. Makefile
  6 +#
  7 +# $MODULE_DIR the module dir. ie. src/os/linux
  8 +# $MODULE_ID the id of module. ie. CORE
  9 +# $MODULE_DEPENDS array, the denpend MODULEs id. ie. (CORE OS)
  10 +# $ModuleLibIncs array, the depend 3rdpart library includes. ie. (objs/st-1.9/obj objs/libx264/obj)
  11 +# $MODULE_FILES array, the head/cpp files of modules. ie. (public log)
  12 +#
  13 +# returns:
  14 +# $MODULE_OBJS array, the objects of the modules, used for link the binary
  15 +
  16 +FILE=${SRS_OBJS}/${SRS_MAKEFILE}
  17 +echo "#####################################################################################" >> ${FILE}
  18 +echo "# the ${MODULE_ID} module." >> ${FILE}
  19 +echo "#####################################################################################" >> ${FILE}
  20 +echo >> ${FILE}
  21 +
  22 +# INCS
  23 +echo "# INCS for ${MODULE_ID}, headers of module and its depends to compile" >> ${FILE}
  24 +#
  25 +# the public include files, for example:
  26 +# CORE_MODULE_INCS = -Isrc/core
  27 +echo "${MODULE_ID}_MODULE_INCS = -I${MODULE_DIR} " >> ${FILE}
  28 +#
  29 +# the private include files, for example:
  30 +# CORE_INCS = -Isrc/core -Iobjs/st -Iobjs -Iobjs/hp -Iobjs
  31 +# MAIN_INCS = -Isrc/main $(CORE_MODULE_INCS) -Iobjs/st -Iobjs
  32 +# where the public will be used for other modules which depends on it.
  33 +INCS_NAME="${MODULE_ID}_INCS"
  34 +#
  35 +# current module header files
  36 +echo -n "${INCS_NAME} = -I${MODULE_DIR} " >> ${FILE}
  37 +#
  38 +# depends module header files
  39 +for item in ${MODULE_DEPENDS[*]}; do
  40 + DEP_INCS_NAME="${item}_INCS"do
  41 + DEP_INCS_NAME="${item}_MODULE_INCS"
  42 + echo -n "\$(${DEP_INCS_NAME}) " >> ${FILE}
  43 +done
  44 +#
  45 +# depends library header files
  46 +for item in ${ModuleLibIncs[*]}; do
  47 + echo -n "-I${item} " >> ${FILE}
  48 +done
  49 +echo "" >> ${FILE}; echo "" >> ${FILE}
  50 +
  51 +# DEPS
  52 +echo "# DEPS for ${MODULE_ID}, the depends of make schema" >> ${FILE}
  53 +# depends on headers of self module, for example:
  54 +# CORE_DEPS = src/core/srs_core.hpp
  55 +DEPS_NAME="${MODULE_ID}_DEPS"
  56 +echo -n "${DEPS_NAME} = " >> ${FILE}
  57 +for item in ${MODULE_FILES[*]}; do
  58 + HEADER_FILE="${MODULE_DIR}/${item}.hpp"
  59 + if [ -f ${HEADER_FILE} ]; then
  60 + echo -n " ${HEADER_FILE}" >> ${FILE}
  61 + fi
  62 +done
  63 +# depends on other modules, for example:
  64 +# MAIN_DEPS = $(CORE_DEPS)
  65 +for item in ${MODULE_DEPENDS[*]}; do
  66 + DEP_DEPS_NAME="${item}_DEPS"
  67 + echo -n " \$(${DEP_DEPS_NAME}) " >> ${FILE}
  68 +done
  69 +echo "" >> ${FILE}; echo "" >> ${FILE}
  70 +
  71 +# OBJ
  72 +echo "# OBJ for ${MODULE_ID}, each object file" >> ${FILE}
  73 +MODULE_OBJS=()
  74 +for item in ${MODULE_FILES[*]}; do
  75 + CPP_FILE="${MODULE_DIR}/${item}.cpp"
  76 + OBJ_FILE="${SRS_OBJS}/${MODULE_DIR}/${item}.o"
  77 + MODULE_OBJS="${MODULE_OBJS[@]} ${CPP_FILE}"
  78 + if [ -f ${CPP_FILE} ]; then
  79 + echo "${OBJ_FILE}: \$(${DEPS_NAME}) ${CPP_FILE} " >> ${FILE}
  80 + echo " \$(CXX) -c \$(CXXFLAGS) \$(${INCS_NAME})\\" >> ${FILE}
  81 + echo " -o ${OBJ_FILE} ${CPP_FILE}" >> ${FILE}
  82 + fi
  83 +done
  84 +echo "" >> ${FILE}
  85 +
  86 +# parent Makefile, to create module output dir before compile it.
  87 +echo " mkdir -p ${SRS_OBJS}/${MODULE_DIR}" >> ${SRS_MAKEFILE}
  88 +
  89 +echo -n "generate module ${MODULE_ID} ok"; echo '!';