├── 3rdparty ├── http-parser-2.1.zip └── st-1.9.zip ├── README.md ├── auto ├── apps.sh └── modules.sh ├── configure ├── src ├── app │ ├── htl_app_hls_load.cpp │ ├── htl_app_hls_load.hpp │ ├── htl_app_http_client.cpp │ ├── htl_app_http_client.hpp │ ├── htl_app_http_load.cpp │ ├── htl_app_http_load.hpp │ ├── htl_app_m3u8_parser.cpp │ ├── htl_app_m3u8_parser.hpp │ ├── htl_app_rtmp_client.cpp │ ├── htl_app_rtmp_client.hpp │ ├── htl_app_rtmp_load.cpp │ ├── htl_app_rtmp_load.hpp │ ├── htl_app_rtmp_protocol.cpp │ ├── htl_app_rtmp_protocol.hpp │ ├── htl_app_task_base.cpp │ └── htl_app_task_base.hpp ├── core │ ├── htl_core_aggregate_ret.cpp │ ├── htl_core_aggregate_ret.hpp │ ├── htl_core_error.cpp │ ├── htl_core_error.hpp │ ├── htl_core_log.cpp │ ├── htl_core_log.hpp │ ├── htl_core_uri.cpp │ ├── htl_core_uri.hpp │ └── htl_stdinc.hpp ├── main │ ├── htl_main_hls_load.cpp │ ├── htl_main_http_load.cpp │ ├── htl_main_rtmp_load.cpp │ ├── htl_main_utility.cpp │ └── htl_main_utility.hpp ├── os │ ├── htl_os_st.cpp │ └── htl_os_st.hpp └── st-load │ ├── init │ └── st-load.upp ├── start_hls_live.sh ├── start_hls_vod.sh ├── start_http.sh └── start_rtmp.sh /3rdparty/http-parser-2.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanhd/st-load/da79647f01bb8c4b13bef7002857a87108896511/3rdparty/http-parser-2.1.zip -------------------------------------------------------------------------------- /3rdparty/st-1.9.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanhd/st-load/da79647f01bb8c4b13bef7002857a87108896511/3rdparty/st-1.9.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | st-load 2 | =========== 3 | 4 | hls/http/rtmp load test tool base on st(state-threads), support huge concurrency
5 | 6 | 服务器负载测试工具(st-load):
7 | 1. 模拟huge并发:2G内存就可以开300k连接。基于states-threads的协程。
8 | 2. 支持HLS解析和测试,下载ts片后等待一个切片长度,模拟客户端。支持HLS点播和直播。
9 | 3. 支持HTTP负载测试,所有并发重复下载一个http文件。可将80Gbps带宽测试的72Gbps。
10 | 4. 支持RTMP流测试,一个进程支持5k并发。使用nginx-rtmp的协议直接将chunk流解析为messgae。
11 | 5. RTMP协议使用高性能服务器SRS([SimpleRtmpServer](https://github.com/winlinvip/simple-rtmp-server))的协议栈,1000个客户端只需要使用30%CPU。
12 | 13 | TestEnvironment: 24CPU, 80Gbps Network, 16GB Memory
14 | Server: NGINX HLS
15 | Result: 90% bandwith, 72Gbps 16 | 17 |
 18 | [root@dell-server ~]# dstat
 19 | ----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
 20 | usr sys idl wai hiq siq| read  writ| recv  send  |  in   out | int   csw 
 21 |   1   1  95   0   0   3|4091B  369k|   0     0   |   0     0 | 100k 9545 
 22 |   3   8  66   0   0  23|   0     0 |  40MB 6114MB|   0     0 | 681k   46k
 23 |   3   8  63   0   0  25|   0   100k|  41MB 6223MB|   0     0 | 692k   46k
 24 |   3   8  64   0   0  25|   0     0 |  41MB 6190MB|   0     0 | 694k   45k
 25 |   3   8  66   0   0  23|   0     0 |  40MB 6272MB|   0     0 | 694k   48k
 26 |   3   8  64   0   0  25|   0    20k|  40MB 6161MB|   0     0 | 687k   46k
 27 |   3   8  65   0   0  24|   0     0 |  40MB 6198MB|   0     0 | 687k   46k
 28 |   3   8  66   0   0  23|   0     0 |  40MB 6231MB|   0     0 | 688k   47k
 29 |   3   7  70   0   0  20|   0    68k|  40MB 6159MB|   0     0 | 675k   49k
 30 |   3   9  62   0   0  26|   0  4096B|  42MB 6283MB|   0     0 | 702k   44k
 31 |   4   8  62   0   0  25|   0  2472k|  40MB 6122MB|   0     0 | 698k   44k
 32 |   3   8  67   0   0  22|   0     0 |  39MB 6066MB|   0     0 | 671k   46k
 33 |   3   8  64   0   0  25|   0     0 |  41MB 6263MB|   0     0 | 695k   46k
 34 |   3   8  64   0   0  25|4096B  132k|  41MB 6161MB|   0     0 | 687k   45k
 35 |   3  11  60   0   0  26|   0     0 |  42MB 6822MB|   0     0 | 714k   36k
 36 |   3  10  62   0   0  25|   0     0 |  40MB 6734MB|   0     0 | 703k   38k
 37 |   3  11  60   0   0  26|   0     0 |  43MB 7019MB|   0     0 | 724k   38k
 38 |   3  11  60   0   0  26|   0    24k|  45MB 7436MB|   0     0 | 746k   41k
 39 |   3  11  60   0   0  27|   0    24k|  47MB 7736MB|   0     0 | 766k   42k
 40 |   3  11  59   0   0  28|   0     0 |  52MB 8283MB|   0     0 | 806k   45k
 41 |   2  10  61   0   0  27|   0     0 |  54MB 8359MB|   0     0 | 806k   47k
 42 |   3  12  53   0   0  32|   0    16k|  58MB 8565MB|   0     0 | 850k   42k
 43 |   2  10  62   0   0  26|   0  1212k|  51MB 8140MB|   0     0 | 783k   47k
 44 |   2  10  64   0   0  24|   0     0 |  42MB 7033MB|   0     0 | 703k   40k
 45 |   2  11  62   0   0  25|   0     0 |  43MB 7203MB|   0     0 | 703k   40k
 46 |   2  12  57   0   0  29|   0     0 |  50MB 7970MB|   0     0 | 774k   40k
 47 |   2  11  54   0   0  33|   0     0 |  72MB 8943MB|   0     0 | 912k   47k
 48 |   3  13  65   0   0  20|   0     0 |  36MB 7247MB|   0     0 | 552k   29k
 49 |   3  14  61   0   0  23|   0  1492k|  42MB 8091MB|   0     0 | 613k   32k
 50 |   3  13  54   0   0  30|   0     0 |  57MB 9144MB|   0     0 | 760k   34k
 51 |   2  10  55   0   0  32|   0    84k|  69MB 9292MB|   0     0 | 861k   38k
 52 |   2   9  58   0   0  31|   0    92k|  71MB 9083MB|   0     0 | 860k   39k
 53 |   2   9  56   0   0  33|   0     0 |  78MB 9098MB|   0     0 | 914k   39k
 54 |   2   8  61   0   0  30|   0     0 |  73MB 8860MB|   0     0 | 876k   39k
 55 | 
56 | 57 | RTMP load test:
58 |
 59 | top - 17:57:24 up  7:10,  7 users,  load average: 0.20, 0.20, 0.09
 60 | Tasks: 154 total,   1 running, 153 sleeping,   0 stopped,   0 zombie
 61 | Cpu(s):  7.4%us,  7.2%sy,  0.0%ni, 78.8%id,  0.0%wa,  0.1%hi,  6.5%si,  0.0%st
 62 | Mem:   2055440k total,  1304528k used,   750912k free,   182336k buffers
 63 | Swap:  2064376k total,        0k used,  2064376k free,   613848k cached
 64 | 
 65 |   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 66 | 13091 winlin    20   0  186m 110m 1404 S 29.6  5.5   1:55.35 ./objs/st_rtmp_load -c 1000 
 67 | 12544 winlin    20   0  124m  22m 2080 S 20.3  1.1   1:51.51 ./objs/simple_rtmp_server
 68 | 
 69 | ----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
 70 | usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
 71 |   7   7  82   0   0   4|   0     0 | 158M  158M|   0     0 |2962   353 
 72 |   6   5  83   0   0   6|   0     0 |  74M   74M|   0     0 |2849   291 
 73 |   7   6  81   0   0   6|   0     0 | 102M  102M|   0     0 |2966   360 
 74 |   7   8  79   0   0   6|   0     0 | 168M  168M|   0     0 |2889   321 
 75 |   7   7  79   0   0   7|   0     0 |  83M   83M|   0     0 |2862   364 
 76 |   5   6  83   0   0   6|   0     0 | 106M  106M|   0     0 |2967   296 
 77 |   5   6  83   0   0   6|   0     0 |  54M   54M|   0     0 |2907   355 
 78 |   6   6  84   0   0   4|   0     0 |  58M   58M|   0     0 |2986   353 
 79 |   6   6  83   0   0   4|   0     0 | 117M  117M|   0     0 |2863   326 
 80 |   7   6  82   0   0   5|   0     0 |  97M   97M|   0     0 |2954   321 
 81 |   5   7  78   2   0   8|   0    40k|  82M   82M|   0     0 |2909   357 
 82 |   5   5  84   0   0   6|   0     0 |  57M   57M|   0     0 |2937   307 
 83 |   8   8  78   0   0   6|   0     0 | 190M  190M|   0     0 |3024   413 
 84 |   5   7  82   0   0   7|   0     0 |  75M   75M|   0     0 |2940   310 
 85 |   8   8  80   0   0   4|   0     0 | 136M  136M|   0     0 |3000   436 
 86 |   8   8  74   0   0  10|   0     0 | 116M  116M|   0     0 |2816   356 
 87 |   7   8  78   0   0   6|   0     0 | 128M  128M|   0     0 |2972   424 
 88 |   6   8  80   0   0   7|   0  4096B| 123M  123M|   0     0 |2981   395 
 89 |   6   6  83   0   0   5|   0     0 |  50M   50M|   0     0 |2984   367 
 90 |   7   6  81   2   0   4|   0    92k|  49M   49M|   0     0 |3010   445 
 91 |   5   6  84   0   0   6|   0     0 |  22M   22M|   0     0 |2912   364 
 92 |   5   5  85   0   0   4|   0     0 |  34M   34M|   0     0 |3001   429 
 93 |   6   6  81   0   0   7|   0     0 |  45M   45M|   0     0 |2996   468 
 94 |   5   5  84   0   0   6|   0     0 |  18M   18M|   0     0 |2923   338 
 95 |   8   8  77   0   0   7|   0     0 | 158M  158M|   0     0 |2971   351 
 96 |   7   7  80   0   0   5|   0     0 | 167M  167M|   0     0 |2860   334 
 97 |   6   5  83   0   0   6|   0    60k|  61M   61M|   0     0 |2988   424 
 98 |   7   8  79   0   0   6|   0     0 | 140M  140M|   0     0 |2916   391 
 99 |   8   8  78   0   0   6|   0     0 | 172M  172M|   0     0 |2961   348 
100 |   7   8  78   0   0   7|   0     0 | 127M  127M|   0     0 |2865   347 
101 |   5   6  84   0   0   5|   0     0 |  73M   73M|   0     0 |2972   344 
102 |   6   8  78   0   0   8|   0     0 | 115M  115M|   0     0 |2942   314 
103 |   7   8  79   0   0   6|   0     0 | 147M  147M|   0     0 |2966   366
104 | 
105 | 106 | -------------------------------------------------------------------------------- /auto/apps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # params: 4 | # $GLOBAL_DIR_OBJS the objs directory. ie. objs 5 | # $GLOBAL_FILE_MAKEFILE the makefile name. ie. Makefile 6 | # $MAIN_ENTRANCES array, all main entrance, disable all except the $APP_MAIN itself 7 | # $MODULE_OBJS array, the objects to compile the app. 8 | # $BUILD_KEY a string indicates the build key for Makefile. ie. dump 9 | # $APP_MAIN the object file that contains main function. ie. your_app_main 10 | # $APP_NAME the app name to output. ie. your_app 11 | # $ModuleLibFiles array, the 3rdpart library file to link with. ie. (objs/st-1.9/obj/libst.a objs/libx264/obj/libx264.a) 12 | # $LINK_OPTIONS the linker options. 13 | # $SO_PATH the libssl.so.10 and other so file path. 14 | 15 | FILE=${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} 16 | 17 | APP_TARGET="${GLOBAL_DIR_OBJS}/${APP_NAME}" 18 | 19 | echo "generate app ${APP_NAME} depends..."; 20 | 21 | echo "# build ${APP_TARGET}" >> ${FILE} 22 | echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE} 23 | 24 | echo -n "${APP_TARGET}: " >> ${FILE} 25 | for item in ${MODULE_OBJS[*]}; do 26 | FILE_NAME=`basename $item` 27 | FILE_NAME=${FILE_NAME%.*} 28 | 29 | ignored=0 30 | for disabled_item in ${MAIN_ENTRANCES[*]}; do 31 | if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then 32 | ignored=1 33 | continue; 34 | fi 35 | done 36 | 37 | if [ ! -f ${item} ]; then 38 | ignored=1 39 | fi 40 | 41 | if [ ${ignored} == 1 ]; then 42 | continue; 43 | fi 44 | 45 | OBJ_FILE=${GLOBAL_DIR_OBJS}/$item 46 | OBJ_FILE="${OBJ_FILE%.*}.o" 47 | echo -n "${OBJ_FILE} " >> ${FILE} 48 | done 49 | echo "" >> ${FILE} 50 | 51 | echo "generate app ${APP_NAME} link..."; 52 | 53 | echo -n " \$(LINK) ${PerformanceLink} -o ${APP_TARGET} " >> ${FILE} 54 | for item in ${MODULE_OBJS[*]}; do 55 | FILE_NAME=`basename $item` 56 | FILE_NAME=${FILE_NAME%.*} 57 | 58 | ignored=0 59 | for disabled_item in ${MAIN_ENTRANCES[*]}; do 60 | if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then 61 | ignored=1 62 | continue; 63 | fi 64 | done 65 | 66 | if [ ! -f ${item} ]; then 67 | ignored=1 68 | fi 69 | 70 | if [ ${ignored} == 1 ]; then 71 | continue; 72 | fi 73 | 74 | OBJ_FILE=${GLOBAL_DIR_OBJS}/$item 75 | OBJ_FILE="${OBJ_FILE%.*}.o" 76 | echo -n "${OBJ_FILE} " >> ${FILE} 77 | done 78 | # 3rdpart library static link. 79 | for item in ${ModuleLibFiles[*]}; do 80 | echo -n "$item " >> ${FILE} 81 | done 82 | # link options. 83 | echo -n "${LINK_OPTIONS}" >> ${FILE} 84 | echo "" >> ${FILE} 85 | 86 | # set the so reference path. 87 | if [[ ! -z ${SO_PATH} ]]; then 88 | echo -n " @bash auto/set_so_rpath.sh ${SOPathTool} ${APP_TARGET} ${SO_PATH}" >> ${FILE} 89 | echo "" >> ${FILE} 90 | fi 91 | 92 | echo -n "generate app ${APP_NAME} ok"; echo '!'; 93 | -------------------------------------------------------------------------------- /auto/modules.sh: -------------------------------------------------------------------------------- 1 | # params: 2 | # $GLOBAL_DIR_OBJS the objs directory. ie. objs 3 | # $GLOBAL_FILE_MAKEFILE the makefile name. ie. Makefile 4 | # $MODULE_DIR the module dir. ie. src/os/linux 5 | # $MODULE_ID the id of module. ie. CORE 6 | # $MODULE_DEPENDS array, the denpend MODULEs id. ie. (CORE OS) 7 | # $ModuleLibIncs array, the depend 3rdpart library includes. ie. (objs/st-1.9/obj objs/libx264/obj) 8 | # $MODULE_FILES array, the head/cpp files of modules. ie. (public log) 9 | # 10 | # returns: 11 | # $MODULE_OBJS array, the objects of the modules. 12 | 13 | FILE=${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} 14 | 15 | # INCS 16 | INCS_NAME="${MODULE_ID}_INCS" 17 | echo "# the ${MODULE_ID} module." >> ${FILE} 18 | echo "${MODULE_ID}_MODULE_INCS = -I ${MODULE_DIR} " >> ${FILE} 19 | echo -n "${INCS_NAME} = -I ${MODULE_DIR} " >> ${FILE} 20 | for item in ${MODULE_DEPENDS[*]}; do 21 | DEP_INCS_NAME="${item}_INCS"do 22 | DEP_INCS_NAME="${item}_MODULE_INCS" 23 | echo -n "\$(${DEP_INCS_NAME}) " >> ${FILE} 24 | done 25 | for item in ${ModuleLibIncs[*]}; do 26 | echo -n "-I ${item} " >> ${FILE} 27 | done 28 | echo "" >> ${FILE} 29 | 30 | # DEPS 31 | DEPS_NAME="${MODULE_ID}_DEPS" 32 | echo -n "${DEPS_NAME} = " >> ${FILE} 33 | for item in ${MODULE_FILES[*]}; do 34 | HEADER_FILE="${MODULE_DIR}/${item}.hpp" 35 | if [ -f ${HEADER_FILE} ]; then 36 | echo -n " ${HEADER_FILE}" >> ${FILE} 37 | fi 38 | done 39 | for item in ${MODULE_DEPENDS[*]}; do 40 | DEP_DEPS_NAME="${item}_DEPS" 41 | echo -n " \$(${DEP_DEPS_NAME}) " >> ${FILE} 42 | done 43 | echo "" >> ${FILE}; echo "" >> ${FILE} 44 | 45 | # OBJ 46 | MODULE_OBJS=() 47 | for item in ${MODULE_FILES[*]}; do 48 | CPP_FILE="${MODULE_DIR}/${item}.cpp" 49 | OBJ_FILE="${GLOBAL_DIR_OBJS}/${MODULE_DIR}/${item}.o" 50 | MODULE_OBJS="${MODULE_OBJS[@]} ${CPP_FILE}" 51 | if [ -f ${CPP_FILE} ]; then 52 | echo "${OBJ_FILE}: \$(${DEPS_NAME}) ${CPP_FILE} " >> ${FILE} 53 | echo " \$(GCC) -c \$(CXXFLAGS) \$(${INCS_NAME}) -o ${OBJ_FILE} ${CPP_FILE}" >> ${FILE} 54 | fi 55 | done 56 | echo "" >> ${FILE} 57 | 58 | # Makefile 59 | echo " mkdir -p ${GLOBAL_DIR_OBJS}/${MODULE_DIR}" >> ${GLOBAL_FILE_MAKEFILE} 60 | 61 | echo -n "generate module ${MODULE_ID} ok"; echo '!'; 62 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GLOBAL_FILE_MAKEFILE="Makefile" 4 | GLOBAL_DIR_OBJS="objs" 5 | 6 | mkdir -p ${GLOBAL_DIR_OBJS} 7 | 8 | ##################################################################################### 9 | # prepare the depends tools 10 | ##################################################################################### 11 | # st-1.9 12 | if [[ -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.a && -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.so ]]; then 13 | echo "st-1.9t is ok."; 14 | else 15 | echo "build st-1.9t"; 16 | (rm -rf ${GLOBAL_DIR_OBJS}/st-1.9 && cd ${GLOBAL_DIR_OBJS} && unzip ../3rdparty/st-1.9.zip && cd st-1.9 && make linux-debug) 17 | fi 18 | # check status 19 | ret=$?; if [[ $ret -ne 0 ]]; then echo "build st-1.9 failed, ret=$ret"; exit $ret; fi 20 | if [ ! -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.a ]; then echo "build st-1.9 failed."; exit -1; fi 21 | if [ ! -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.so ]; then echo "build st-1.9 failed."; exit -1; fi 22 | 23 | # http-parser-2.1 24 | if [[ -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/http_parser.h && -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then 25 | echo "http-parser-2.1 is ok."; 26 | else 27 | echo "build http-parser-2.1"; 28 | ( 29 | rm -rf ${GLOBAL_DIR_OBJS}/http-parser-2.1 && cd ${GLOBAL_DIR_OBJS} && unzip ../3rdparty/http-parser-2.1.zip && 30 | cd http-parser-2.1 && 31 | sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile && 32 | sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile && 33 | make package 34 | ) 35 | fi 36 | # check status 37 | ret=$?; if [[ $ret -ne 0 ]]; then echo "build http-parser-2.1 failed, ret=$ret"; exit $ret; fi 38 | if [[ ! -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/http_parser.h ]]; then echo "build http-parser-2.1 failed"; exit -1; fi 39 | if [[ ! -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then echo "build http-parser-2.1 failed"; exit -1; fi 40 | 41 | ##################################################################################### 42 | # generate Makefile. 43 | ##################################################################################### 44 | echo "generate Makefile" 45 | 46 | cat << END > ${GLOBAL_FILE_MAKEFILE} 47 | .PHONY: default help clean http hls all _prepare_dir 48 | default: all 49 | 50 | help: 51 | @echo "Usage: make |||||" 52 | @echo " help display this help menu" 53 | @echo " clean cleanup project" 54 | @echo " http build the http load test tool over st(state-threads)" 55 | @echo " hls build the hls load test tool over st(state-threads)" 56 | @echo " rtmp build the rtmp load test tool over st(state-threads)" 57 | @echo " all build the http/hls load test tool over st(state-threads)" 58 | 59 | clean: 60 | (cd ${GLOBAL_DIR_OBJS}; rm -rf src st_*_load) 61 | 62 | http: _prepare_dir 63 | @echo "build the http load test tool over st(state-threads)" 64 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_http_load 65 | 66 | rtmp: _prepare_dir 67 | @echo "build the http load test tool over st(state-threads)" 68 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_rtmp_load 69 | 70 | hls: _prepare_dir 71 | @echo "build the HLS load test tool over st(state-threads)" 72 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_hls_load 73 | 74 | all: _prepare_dir 75 | @echo "build the http/hls/rtmp load test tool over st(state-threads)" 76 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_http_load 77 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_hls_load 78 | \$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} st_rtmp_load 79 | 80 | # the ./configure will generate it. 81 | _prepare_dir: 82 | END 83 | 84 | echo 'generate Makefile ok!' 85 | 86 | # the performance analysis, uncomments the following when use gperf to analysis the performance. see third-party/readme.txt 87 | #Performance="-pg" 88 | #PerformanceLink="-pg" 89 | # enable gdb debug 90 | GDBDebug="-g -O0" 91 | # the warning level. 92 | WarnLevel="-Wall -Wextra" 93 | # the compile standard. 94 | CppStd="-std=c++98" 95 | # other macros defined 96 | UserMacros="" 97 | # the cxx flag generated. 98 | CXXFLAGS="${CppStd} ${WarnLevel} ${GDBDebug} ${Performance} ${UserMacros}" 99 | cat << END > ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} 100 | CXXFLAGS = ${CXXFLAGS} 101 | GCC = g++ 102 | LINK = \$(GCC) 103 | AR = ar 104 | 105 | .PHONY: default st_http_load st_rtmp_load st_hls_load 106 | 107 | default: 108 | 109 | END 110 | 111 | # Libraries 112 | LibSTRoot="${GLOBAL_DIR_OBJS}/st-1.9/obj" 113 | LibSTfile="${LibSTRoot}/libst.a" 114 | LibHttpParserRoot="${GLOBAL_DIR_OBJS}/http-parser-2.1" 115 | LibHttpParserfile="${LibHttpParserRoot}/libhttp_parser.a" 116 | 117 | #Core Module 118 | MODULE_ID="CORE" 119 | MODULE_DEPENDS=() 120 | ModuleLibIncs=(${LibHttpParserRoot}) 121 | MODULE_FILES=("htl_core_log" "htl_core_error" "htl_core_uri" "htl_core_aggregate_ret") 122 | MODULE_DIR="src/core" . auto/modules.sh 123 | CORE_OBJS="${MODULE_OBJS[@]}" 124 | 125 | #OS Module 126 | MODULE_ID="OS" 127 | MODULE_DEPENDS=("CORE") 128 | ModuleLibIncs=(${LibSTRoot}) 129 | MODULE_FILES=("htl_os_st") 130 | MODULE_DIR="src/os" . auto/modules.sh 131 | OS_OBJS="${MODULE_OBJS[@]}" 132 | 133 | #APP Module 134 | MODULE_ID="APP" 135 | MODULE_DEPENDS=("CORE" "OS") 136 | ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot}) 137 | MODULE_FILES=("htl_app_hls_load" "htl_app_http_load" "htl_app_http_client" "htl_app_rtmp_client" 138 | "htl_app_m3u8_parser" "htl_app_task_base" "htl_app_rtmp_load" "htl_app_rtmp_protocol") 139 | MODULE_DIR="src/app" . auto/modules.sh 140 | APP_OBJS="${MODULE_OBJS[@]}" 141 | 142 | #Main Module 143 | MODULE_ID="MAIN" 144 | MODULE_DEPENDS=("CORE" "OS" "APP") 145 | ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot}) 146 | MODULE_FILES=("htl_main_hls_load" "htl_main_http_load" "htl_main_rtmp_load" "htl_main_utility") 147 | MODULE_DIR="src/main" . auto/modules.sh 148 | MAIN_OBJS="${MODULE_OBJS[@].o}" 149 | 150 | # all main entrances 151 | MAIN_ENTRANCES=("htl_main_hls_load" "htl_main_http_load" "htl_main_rtmp_load") 152 | 153 | # http load test tool over st(state-threads) 154 | ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile}) 155 | MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}" 156 | BUILD_KEY="st_http_load" APP_MAIN="htl_main_http_load" APP_NAME="st_http_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh 157 | 158 | # rtmp load test tool over st(state-threads) 159 | ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile}) 160 | MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}" 161 | BUILD_KEY="st_rtmp_load" APP_MAIN="htl_main_rtmp_load" APP_NAME="st_rtmp_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh 162 | 163 | # hls load test tool over direct TCP. 164 | ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile}) 165 | MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}" 166 | BUILD_KEY="st_hls_load" APP_MAIN="htl_main_hls_load" APP_NAME="st_hls_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh 167 | 168 | echo 'configure ok! ' 169 | 170 | # next step. 171 | echo "you can:" 172 | echo "\" make \" to build the http/hls load test tools." 173 | echo "\" make help \" to get the usage of make" 174 | -------------------------------------------------------------------------------- /src/app/htl_app_hls_load.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | 20 | #define DEFAULT_TS_DURATION 10 21 | 22 | StHlsTask::StHlsTask(){ 23 | target_duration = DEFAULT_TS_DURATION; 24 | } 25 | 26 | StHlsTask::~StHlsTask(){ 27 | } 28 | 29 | int StHlsTask::Initialize(std::string http_url, bool vod, double startup, double delay, double error, int count){ 30 | int ret = ERROR_SUCCESS; 31 | 32 | is_vod = vod; 33 | 34 | if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){ 35 | return ret; 36 | } 37 | 38 | return ret; 39 | } 40 | 41 | Uri* StHlsTask::GetUri(){ 42 | return &url; 43 | } 44 | 45 | int StHlsTask::ProcessTask(){ 46 | int ret = ERROR_SUCCESS; 47 | 48 | Trace("start to process HLS task #%d, schema=%s, host=%s, port=%d, path=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d", 49 | GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetPath(), startup_seconds, delay_seconds, error_seconds, count); 50 | 51 | StHttpClient client; 52 | 53 | // if count is zero, infinity loop. 54 | for(int i = 0; count == 0 || i < count; i++){ 55 | statistic->OnTaskStart(GetId(), url.GetUrl()); 56 | 57 | if((ret = ProcessM3u8(client)) != ERROR_SUCCESS){ 58 | statistic->OnTaskError(GetId(), 0); 59 | 60 | Error("http client process m3u8 failed. ret=%d", ret); 61 | st_usleep((st_utime_t)(error_seconds * 1000 * 1000)); 62 | continue; 63 | } 64 | 65 | Info("[HLS] %s download completed.", url.GetUrl()); 66 | } 67 | 68 | return ret; 69 | } 70 | 71 | int StHlsTask::ProcessM3u8(StHttpClient& client){ 72 | int ret = ERROR_SUCCESS; 73 | 74 | string m3u8; 75 | if((ret = client.DownloadString(&url, &m3u8)) != ERROR_SUCCESS){ 76 | Error("http client get m3u8 failed. ret=%d", ret); 77 | return ret; 78 | } 79 | Trace("[HLS] get m3u8 %s get success, length=%"PRId64, url.GetUrl(), (int64_t)m3u8.length()); 80 | 81 | vector ts_objects; 82 | if((ret = HlsM3u8Parser::ParseM3u8Data(&url, m3u8, ts_objects, target_duration)) != ERROR_SUCCESS){ 83 | Error("http client parse m3u8 content failed. ret=%d", ret); 84 | return ret; 85 | } 86 | 87 | if((ret = ProcessTS(client, ts_objects)) != ERROR_SUCCESS){ 88 | Error("http client download m3u8 ts file failed. ret=%d", ret); 89 | return ret; 90 | } 91 | 92 | return ret; 93 | } 94 | 95 | int StHlsTask::ProcessTS(StHttpClient& client, vector& ts_objects){ 96 | int ret = ERROR_SUCCESS; 97 | 98 | vector::iterator ite = ts_objects.begin(); 99 | 100 | // if live(not vod), remember the last download ts object. 101 | // if vod(not live), always access from the frist ts. 102 | if(!is_vod){ 103 | ite = find(ts_objects.begin(), ts_objects.end(), last_downloaded_ts); 104 | 105 | // not found, reset to begin to process all. 106 | if(ite == ts_objects.end()){ 107 | ite = ts_objects.begin(); 108 | } 109 | // fount, skip it. 110 | else{ 111 | ite++; 112 | } 113 | 114 | // no ts now, wait for a segment 115 | if(ite == ts_objects.end()){ 116 | int sleep_ms = StUtility::BuildRandomMTime((target_duration > 0)? target_duration:DEFAULT_TS_DURATION); 117 | Trace("[TS] no fresh ts, wait for a while. sleep %dms", sleep_ms); 118 | st_usleep(sleep_ms * 1000); 119 | 120 | return ret; 121 | } 122 | } 123 | 124 | AggregateRet aggregate_ret; 125 | 126 | // to process from the specified ite 127 | for(; ite != ts_objects.end(); ++ite){ 128 | M3u8TS ts_object = *ite; 129 | 130 | if(!is_vod){ 131 | last_downloaded_ts = ts_object; 132 | } 133 | 134 | Info("start to process ts %s", ts_object.ts_url.c_str()); 135 | 136 | aggregate_ret.Add(DownloadTS(client, ts_object)); 137 | } 138 | 139 | return aggregate_ret.GetReturnValue(); 140 | } 141 | 142 | int StHlsTask::DownloadTS(StHttpClient& client, M3u8TS& ts){ 143 | int ret = ERROR_SUCCESS; 144 | 145 | HttpUrl url; 146 | 147 | if((ret = url.Initialize(ts.ts_url)) != ERROR_SUCCESS){ 148 | Error("initialize ts url failed. ret=%d", ret); 149 | return ret; 150 | } 151 | 152 | Info("[TS] url=%s, duration=%.2f, delay=%.2f", url.GetUrl(), ts.duration, delay_seconds); 153 | statistic->OnSubTaskStart(GetId(), ts.ts_url); 154 | 155 | if((ret = client.DownloadString(&url, NULL)) != ERROR_SUCCESS){ 156 | statistic->OnSubTaskError(GetId(), (int)ts.duration); 157 | 158 | Error("http client download ts file %s failed. ret=%d", url.GetUrl(), ret); 159 | return ret; 160 | } 161 | 162 | int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:ts.duration); 163 | Trace("[TS] url=%s download, duration=%.2f, delay=%.2f, size=%"PRId64", sleep %dms", 164 | url.GetUrl(), ts.duration, delay_seconds, client.GetResponseHeader()->content_length, sleep_ms); 165 | st_usleep(sleep_ms * 1000); 166 | 167 | statistic->OnSubTaskEnd(GetId(), (int)ts.duration); 168 | 169 | return ret; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /src/app/htl_app_hls_load.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_hls_load_hpp 2 | #define _htl_app_hls_load_hpp 3 | 4 | /* 5 | #include 6 | */ 7 | #include 8 | #include 9 | #include 10 | 11 | // for http task. 12 | class StHlsTask : public StBaseTask 13 | { 14 | private: 15 | HttpUrl url; 16 | // the last downloaded ts url to prevent download multile times. 17 | M3u8TS last_downloaded_ts; 18 | int target_duration; 19 | bool is_vod; 20 | public: 21 | StHlsTask(); 22 | virtual ~StHlsTask(); 23 | public: 24 | virtual int Initialize(std::string http_url, bool vod, double startup, double delay, double error, int count); 25 | protected: 26 | virtual Uri* GetUri(); 27 | virtual int ProcessTask(); 28 | private: 29 | virtual int ProcessM3u8(StHttpClient& client); 30 | virtual int ProcessTS(StHttpClient& client, std::vector& ts_objects); 31 | virtual int DownloadTS(StHttpClient& client, M3u8TS& ts); 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/app/htl_app_http_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | StHttpClient::StHttpClient(){ 16 | socket = new StSocket(); 17 | connected_url = NULL; 18 | } 19 | 20 | StHttpClient::~StHttpClient(){ 21 | delete socket; 22 | socket = NULL; 23 | 24 | delete connected_url; 25 | connected_url = NULL; 26 | } 27 | 28 | int StHttpClient::DownloadString(HttpUrl* url, std::string* response){ 29 | int ret = ERROR_SUCCESS; 30 | 31 | if((ret = CheckUrl(url)) != ERROR_SUCCESS){ 32 | Error("http client check url failed. ret=%d", ret); 33 | return ret; 34 | } 35 | 36 | if((ret = Connect(url)) != ERROR_SUCCESS){ 37 | Error("http client connect url failed. ret=%d", ret); 38 | return ret; 39 | } 40 | 41 | // send GET request to read content 42 | // GET %s HTTP/1.1\r\nHost: %s\r\n\r\n 43 | stringstream ss; 44 | ss << "GET " << url->GetPath() << " " 45 | << "HTTP/1.1\r\n" 46 | << "Host: " << url->GetHost() << "\r\n" 47 | << "Connection: Keep-Alive" << "\r\n" 48 | << "User-Agent: " << ProductHTTPName << "\r\n" 49 | << "\r\n"; 50 | 51 | ssize_t nwrite; 52 | if((ret = socket->Write(ss.str().c_str(), ss.str().length(), &nwrite)) != ERROR_SUCCESS){ 53 | Error("write to server failed. ret=%d", ret); 54 | return ret; 55 | } 56 | 57 | if((ret = ParseResponse(url, response)) != ERROR_SUCCESS){ 58 | Error("http client parse response failed. ret=%d", ret); 59 | return ret; 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | http_parser* StHttpClient::GetResponseHeader(){ 66 | return &http_header; 67 | } 68 | 69 | int StHttpClient::on_headers_complete(http_parser* parser){ 70 | StHttpClient* obj = (StHttpClient*)parser->data; 71 | obj->OnHeaderCompleted(parser); 72 | 73 | // see http_parser.c:1570, return 1 to skip body. 74 | return 1; 75 | } 76 | 77 | void StHttpClient::OnHeaderCompleted(http_parser* parser){ 78 | // save the parser status when header parse completed. 79 | memcpy(&http_header, parser, sizeof(http_header)); 80 | } 81 | 82 | int StHttpClient::ParseResponse(HttpUrl* url, string* response){ 83 | int ret = ERROR_SUCCESS; 84 | 85 | int body_received = 0; 86 | if((ret = ParseResponseHeader(response, body_received)) != ERROR_SUCCESS){ 87 | Error("parse response header failed. ret=%d", ret); 88 | return ret; 89 | } 90 | 91 | if((ret = ParseResponseBody(url, response, body_received)) != ERROR_SUCCESS){ 92 | Error("parse response body failed. ret=%d", ret); 93 | return ret; 94 | } 95 | 96 | Info("url %s download, body size=%"PRId64, url->GetUrl(), http_header.content_length); 97 | 98 | return ret; 99 | } 100 | 101 | int StHttpClient::ParseResponseBody(HttpUrl* url, string* response, int body_received){ 102 | int ret = ERROR_SUCCESS; 103 | 104 | assert(url != NULL); 105 | 106 | uint64_t body_left = http_header.content_length - body_received; 107 | 108 | if(response != NULL){ 109 | char buf[HTTP_BODY_BUFFER]; 110 | return ParseResponseBodyData(url, response, (size_t)body_left, (const void*)buf, (size_t)HTTP_BODY_BUFFER); 111 | } 112 | else{ 113 | // if ignore response, use shared fast memory. 114 | static char buf[HTTP_BODY_BUFFER]; 115 | return ParseResponseBodyData(url, response, (size_t)body_left, (const void*)buf, (size_t)HTTP_BODY_BUFFER); 116 | } 117 | 118 | return ret; 119 | } 120 | 121 | int StHttpClient::ParseResponseBodyData(HttpUrl* url, string* response, size_t body_left, const void* buf, size_t size){ 122 | int ret = ERROR_SUCCESS; 123 | 124 | assert(url != NULL); 125 | 126 | while(body_left > 0){ 127 | ssize_t nread; 128 | if((ret = socket->Read(buf, (size < body_left)? size:body_left, &nread)) != ERROR_SUCCESS){ 129 | Error("read header from server failed. ret=%d", ret); 130 | return ret; 131 | } 132 | 133 | if(response != NULL && nread > 0){ 134 | response->append((char*)buf, nread); 135 | } 136 | 137 | body_left -= nread; 138 | Info("read url(%s) content partial %"PRId64"/%"PRId64"", 139 | url->GetUrl(), http_header.content_length - body_left, http_header.content_length); 140 | } 141 | 142 | return ret; 143 | } 144 | 145 | int StHttpClient::ParseResponseHeader(string* response, int& body_received){ 146 | int ret = ERROR_SUCCESS; 147 | 148 | http_parser_settings settings; 149 | 150 | memset(&settings, 0, sizeof(settings)); 151 | settings.on_headers_complete = on_headers_complete; 152 | 153 | http_parser parser; 154 | http_parser_init(&parser, HTTP_RESPONSE); 155 | // callback object ptr. 156 | parser.data = (void*)this; 157 | 158 | // reset response header. 159 | memset(&http_header, 0, sizeof(http_header)); 160 | 161 | // parser header. 162 | char buf[HTTP_HEADER_BUFFER]; 163 | for(;;){ 164 | ssize_t nread; 165 | if((ret = socket->Read((const void*)buf, (size_t)sizeof(buf), &nread)) != ERROR_SUCCESS){ 166 | Error("read body from server failed. ret=%d", ret); 167 | return ret; 168 | } 169 | 170 | ssize_t nparsed = http_parser_execute(&parser, &settings, buf, nread); 171 | Info("read_size=%d, nparsed=%d", (int)nread, (int)nparsed); 172 | 173 | // check header size. 174 | if(http_header.nread != 0){ 175 | body_received = nread - nparsed; 176 | 177 | Info("http header parsed, size=%d, content-length=%"PRId64", body-received=%d", 178 | http_header.nread, http_header.content_length, body_received); 179 | 180 | if(response != NULL && body_received > 0){ 181 | response->append(buf + nparsed, body_received); 182 | } 183 | 184 | return ret; 185 | } 186 | 187 | if(nparsed != nread){ 188 | ret = ERROR_HP_PARSE_RESPONSE; 189 | Error("parse response error, parsed(%d)!=read(%d), ret=%d", (int)nparsed, (int)nread, ret); 190 | return ret; 191 | } 192 | } 193 | 194 | return ret; 195 | } 196 | 197 | int StHttpClient::Connect(HttpUrl* url){ 198 | int ret = ERROR_SUCCESS; 199 | 200 | if(socket->Status() == SocketConnected){ 201 | return ret; 202 | } 203 | 204 | string ip; 205 | if((ret = StUtility::DnsResolve(url->GetHost(), ip)) != ERROR_SUCCESS){ 206 | Error("dns resolve failed. ret=%d", ret); 207 | return ret; 208 | } 209 | 210 | if((ret = socket->Connect(ip.c_str(), url->GetPort())) != ERROR_SUCCESS){ 211 | Error("connect to server failed. ret=%d", ret); 212 | return ret; 213 | } 214 | 215 | Info("socket connected on url %s", url->GetUrl()); 216 | 217 | return ret; 218 | } 219 | 220 | int StHttpClient::CheckUrl(HttpUrl* url){ 221 | int ret = ERROR_SUCCESS; 222 | 223 | if(connected_url == NULL){ 224 | connected_url = url->Copy(); 225 | 226 | if((ret = StUtility::DnsResolve(connected_url->GetHost(), connected_ip)) != ERROR_SUCCESS){ 227 | return ret; 228 | } 229 | } 230 | 231 | string ip; 232 | if((ret = StUtility::DnsResolve(url->GetHost(), ip)) != ERROR_SUCCESS){ 233 | return ret; 234 | } 235 | 236 | if(ip != connected_ip || connected_url->GetPort() != url->GetPort()){ 237 | ret = ERROR_HP_EP_CHNAGED; 238 | Error("invalid url=%s, endpoint change from %s:%d to %s:%d", 239 | url->GetUrl(), connected_ip.c_str(), connected_url->GetPort(), ip.c_str(), url->GetPort()); 240 | 241 | return ret; 242 | } 243 | 244 | return ret; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /src/app/htl_app_http_client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_http_client_hpp 2 | #define _htl_app_http_client_hpp 3 | 4 | /* 5 | #include 6 | */ 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | class StHttpClient 13 | { 14 | private: 15 | std::string connected_ip; 16 | HttpUrl* connected_url; 17 | StSocket* socket; 18 | private: 19 | http_parser http_header; 20 | public: 21 | StHttpClient(); 22 | virtual ~StHttpClient(); 23 | public: 24 | /** 25 | * download the content specified by url using HTTP GET method. 26 | * @response the string pointer which store the content. ignore content if set to NULL. 27 | */ 28 | virtual int DownloadString(HttpUrl* url, std::string* response); 29 | public: 30 | /** 31 | * when get response and parse header completed, return the header. 32 | * if not parsed, header set to zero. 33 | */ 34 | virtual http_parser* GetResponseHeader(); 35 | private: 36 | static int on_headers_complete(http_parser* parser); 37 | virtual void OnHeaderCompleted(http_parser* parser); 38 | private: 39 | virtual int ParseResponse(HttpUrl* url, string* response); 40 | virtual int ParseResponseBody(HttpUrl* url, string* response, int body_received); 41 | virtual int ParseResponseBodyData(HttpUrl* url, string* response, size_t body_left, const void* buf, size_t size); 42 | virtual int ParseResponseHeader(string* response, int& body_received); 43 | virtual int Connect(HttpUrl* url); 44 | virtual int CheckUrl(HttpUrl* url); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/app/htl_app_http_load.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | StHttpTask::StHttpTask(){ 17 | } 18 | 19 | StHttpTask::~StHttpTask(){ 20 | } 21 | 22 | int StHttpTask::Initialize(std::string http_url, double startup, double delay, double error, int count){ 23 | int ret = ERROR_SUCCESS; 24 | 25 | if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){ 26 | return ret; 27 | } 28 | 29 | return ret; 30 | } 31 | 32 | Uri* StHttpTask::GetUri(){ 33 | return &url; 34 | } 35 | 36 | int StHttpTask::ProcessTask(){ 37 | int ret = ERROR_SUCCESS; 38 | 39 | Trace("start to process HTTP task #%d, schema=%s, host=%s, port=%d, path=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d", 40 | GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetPath(), startup_seconds, delay_seconds, error_seconds, count); 41 | 42 | StHttpClient client; 43 | 44 | // if count is zero, infinity loop. 45 | for(int i = 0; count == 0 || i < count; i++){ 46 | statistic->OnTaskStart(GetId(), url.GetUrl()); 47 | 48 | if((ret = client.DownloadString(&url, NULL)) != ERROR_SUCCESS){ 49 | statistic->OnTaskError(GetId(), 0); 50 | 51 | Error("http client get url failed. ret=%d", ret); 52 | st_usleep((st_utime_t)(error_seconds * 1000 * 1000)); 53 | continue; 54 | } 55 | 56 | int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0); 57 | Trace("[HTTP] %s download, size=%"PRId64", sleep %dms", url.GetUrl(), client.GetResponseHeader()->content_length, sleep_ms); 58 | st_usleep(sleep_ms * 1000); 59 | 60 | statistic->OnTaskEnd(GetId(), 0); 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/app/htl_app_http_load.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_http_load_hpp 2 | #define _htl_app_http_load_hpp 3 | 4 | /* 5 | #include 6 | */ 7 | #include 8 | 9 | // for http task. 10 | class StHttpTask : public StBaseTask 11 | { 12 | private: 13 | HttpUrl url; 14 | public: 15 | StHttpTask(); 16 | virtual ~StHttpTask(); 17 | public: 18 | virtual int Initialize(std::string http_url, double startup, double delay, double error, int count); 19 | protected: 20 | virtual Uri* GetUri(); 21 | virtual int ProcessTask(); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/app/htl_app_m3u8_parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | class String 18 | { 19 | private: 20 | string value; 21 | public: 22 | String(string str = ""){ 23 | value = str; 24 | } 25 | public: 26 | String& set_str(string str){ 27 | value = str; 28 | return *this; 29 | } 30 | int length(){ 31 | return (int)value.length(); 32 | } 33 | bool startswith(string key, String* left = NULL){ 34 | size_t pos = value.find(key); 35 | 36 | if(pos == 0 && left != NULL){ 37 | left->set_str(value.substr(pos + key.length())); 38 | left->strip(); 39 | } 40 | 41 | return pos == 0; 42 | } 43 | bool endswith(string key, String* left = NULL){ 44 | size_t pos = value.rfind(key); 45 | 46 | if(pos == value.length() - key.length() && left != NULL){ 47 | left->set_str(value.substr(pos)); 48 | left->strip(); 49 | } 50 | 51 | return pos == value.length() - key.length(); 52 | } 53 | String& strip(){ 54 | while(value.length() > 0){ 55 | if(startswith("\n")){ 56 | value = value.substr(1); 57 | continue; 58 | } 59 | 60 | if(startswith("\r")){ 61 | value = value.substr(1); 62 | continue; 63 | } 64 | 65 | if(startswith(" ")){ 66 | value = value.substr(1); 67 | continue; 68 | } 69 | 70 | if(endswith("\n")){ 71 | value = value.substr(0, value.length() - 1); 72 | continue; 73 | } 74 | 75 | if(endswith("\r")){ 76 | value = value.substr(0, value.length() - 1); 77 | continue; 78 | } 79 | 80 | if(endswith(" ")){ 81 | value = value.substr(0, value.length() - 1); 82 | continue; 83 | } 84 | 85 | break; 86 | } 87 | 88 | return *this; 89 | } 90 | string getline(){ 91 | size_t pos = string::npos; 92 | 93 | if((pos = value.find("\n")) != string::npos){ 94 | return value.substr(0, pos); 95 | } 96 | 97 | return value; 98 | } 99 | String& remove(int size){ 100 | if(size >= (int)value.length()){ 101 | value = ""; 102 | return *this; 103 | } 104 | 105 | value = value.substr(size); 106 | return *this; 107 | } 108 | const char* c_str(){ 109 | return value.c_str(); 110 | } 111 | }; 112 | 113 | HlsM3u8Parser::HlsM3u8Parser(){ 114 | } 115 | 116 | HlsM3u8Parser::~HlsM3u8Parser(){ 117 | } 118 | 119 | int HlsM3u8Parser::ParseM3u8Data(HttpUrl* url, string m3u8, vector& ts_objects, int& target_duration){ 120 | int ret = ERROR_SUCCESS; 121 | 122 | String data(m3u8); 123 | 124 | // http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.3.1 125 | // An Extended M3U file is distinguished from a basic M3U file by its 126 | // first line, which MUST be the tag #EXTM3U. 127 | if(!data.startswith("#EXTM3U")){ 128 | ret = ERROR_HLS_INVALID; 129 | Error("invalid hls, #EXTM3U not found. ret=%d", ret); 130 | return ret; 131 | } 132 | 133 | String value; 134 | 135 | M3u8TS ts_object; 136 | while(data.length() > 0){ 137 | String line; 138 | data.remove(line.set_str(data.strip().getline()).strip().length()).strip(); 139 | 140 | // http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.2 141 | // #EXT-X-TARGETDURATION: 142 | // where s is an integer indicating the target duration in seconds. 143 | if(line.startswith("#EXT-X-TARGETDURATION:", &value)){ 144 | target_duration = atoi(value.c_str()); 145 | ts_object.duration = target_duration; 146 | continue; 147 | } 148 | 149 | // http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.3.2 150 | // #EXTINF:, 151 | // "duration" is an integer or floating-point number in decimal 152 | // positional notation that specifies the duration of the media segment 153 | // in seconds. Durations that are reported as integers SHOULD be 154 | // rounded to the nearest integer. Durations MUST be integers if the 155 | // protocol version of the Playlist file is less than 3. The remainder 156 | // of the line following the comma is an optional human-readable 157 | // informative title of the media segment. 158 | // ignore others util EXTINF 159 | if(line.startswith("#EXTINF:", &value)){ 160 | ts_object.duration = atof(value.c_str()); 161 | continue; 162 | } 163 | 164 | if(!line.startswith("#")){ 165 | ts_object.ts_url = url->Resolve(line.c_str()); 166 | ts_objects.push_back(ts_object); 167 | continue; 168 | } 169 | } 170 | 171 | return ret; 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/app/htl_app_m3u8_parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_m3u8_parser_hpp 2 | #define _htl_app_m3u8_parser_hpp 3 | 4 | /* 5 | #include <htl_app_m3u8_parser.hpp> 6 | */ 7 | #include <string> 8 | #include <vector> 9 | 10 | #include <htl_core_uri.hpp> 11 | 12 | struct M3u8TS 13 | { 14 | std::string ts_url; 15 | double duration; 16 | 17 | bool operator== (const M3u8TS& b)const{ 18 | return ts_url == b.ts_url; 19 | } 20 | }; 21 | 22 | class HlsM3u8Parser 23 | { 24 | public: 25 | HlsM3u8Parser(); 26 | virtual ~HlsM3u8Parser(); 27 | public: 28 | static int ParseM3u8Data(HttpUrl* url, std::string m3u8, std::vector<M3u8TS>& ts_objects, int& target_duration); 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/app/htl_app_rtmp_client.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <inttypes.h> 4 | #include <assert.h> 5 | 6 | #include <string> 7 | #include <sstream> 8 | using namespace std; 9 | 10 | #include <htl_core_error.hpp> 11 | #include <htl_core_log.hpp> 12 | 13 | #include <htl_app_rtmp_client.hpp> 14 | #include <htl_app_rtmp_protocol.hpp> 15 | 16 | StRtmpClient::StRtmpClient(){ 17 | socket = new StSocket(); 18 | stream_id = 0; 19 | srs = NULL; 20 | } 21 | 22 | StRtmpClient::~StRtmpClient(){ 23 | delete socket; 24 | delete srs; 25 | socket = NULL; 26 | } 27 | 28 | int StRtmpClient::Dump(RtmpUrl* url){ 29 | int ret = ERROR_SUCCESS; 30 | 31 | if((ret = Connect(url)) != ERROR_SUCCESS){ 32 | Error("rtmp client connect server failed. ret=%d", ret); 33 | return ret; 34 | } 35 | 36 | if((ret = Handshake()) != ERROR_SUCCESS){ 37 | Error("rtmp client handshake failed. ret=%d", ret); 38 | return ret; 39 | } 40 | Info("rtmp client handshake success"); 41 | 42 | if((ret = ConnectApp(url)) != ERROR_SUCCESS){ 43 | Error("rtmp client connect tcUrl failed. ret=%d", ret); 44 | return ret; 45 | } 46 | Info("rtmp client connect tcUrl(%s) success", url->GetTcUrl()); 47 | 48 | if((ret = CreateStream()) != ERROR_SUCCESS){ 49 | Error("rtmp client create stream failed. ret=%d", ret); 50 | return ret; 51 | } 52 | Info("rtmp client create stream(%d) success", stream_id); 53 | 54 | if((ret = PlayStram(url)) != ERROR_SUCCESS){ 55 | Error("rtmp client play stream failed. ret=%d", ret); 56 | return ret; 57 | } 58 | Info("rtmp client play stream(%s) success", url->GetUrl()); 59 | 60 | if((ret = DumpAV()) != ERROR_SUCCESS){ 61 | Error("rtmp client dump av failed. ret=%d", ret); 62 | return ret; 63 | } 64 | Info("rtmp client dump av success"); 65 | 66 | return ret; 67 | } 68 | 69 | int StRtmpClient::Connect(RtmpUrl* url){ 70 | int ret = ERROR_SUCCESS; 71 | 72 | if(socket->Status() == SocketConnected){ 73 | return ret; 74 | } 75 | 76 | string ip; 77 | if((ret = StUtility::DnsResolve(url->GetHost(), ip)) != ERROR_SUCCESS){ 78 | Error("dns resolve failed. ret=%d", ret); 79 | return ret; 80 | } 81 | 82 | if((ret = socket->Connect(ip.c_str(), url->GetPort())) != ERROR_SUCCESS){ 83 | Error("connect to server failed. ret=%d", ret); 84 | return ret; 85 | } 86 | 87 | Info("socket connected on url %s", url->GetUrl()); 88 | 89 | delete srs; 90 | srs = new SrsRtmpClient(socket->GetStfd()); 91 | 92 | return ret; 93 | } 94 | 95 | int StRtmpClient::Handshake(){ 96 | return srs->handshake(); 97 | } 98 | 99 | int StRtmpClient::ConnectApp(RtmpUrl* url){ 100 | return srs->connect_app(url->GetApp(), url->GetTcUrl()); 101 | } 102 | 103 | int StRtmpClient::CreateStream(){ 104 | return srs->create_stream(stream_id); 105 | } 106 | 107 | int StRtmpClient::PlayStram(RtmpUrl* url){ 108 | return srs->play(url->GetStream(), stream_id); 109 | } 110 | 111 | int StRtmpClient::DumpAV(){ 112 | int ret = ERROR_SUCCESS; 113 | 114 | // recv response 115 | while(true){ 116 | SrsCommonMessage* msg = NULL; 117 | if ((ret = srs->recv_message(&msg)) != ERROR_SUCCESS){ 118 | return ret; 119 | } 120 | SrsAutoFree(SrsCommonMessage, msg, false); 121 | 122 | Info("get message type=%d, size=%d", 123 | msg->header.message_type, msg->header.payload_length); 124 | } 125 | 126 | return ret; 127 | } 128 | -------------------------------------------------------------------------------- /src/app/htl_app_rtmp_client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_rtmp_client_hpp 2 | #define _htl_app_rtmp_client_hpp 3 | 4 | /* 5 | #include <htl_app_rtmp_client.hpp> 6 | */ 7 | #include <string> 8 | 9 | #include <htl_core_uri.hpp> 10 | #include <htl_os_st.hpp> 11 | 12 | class SrsRtmpClient; 13 | 14 | class StRtmpClient 15 | { 16 | private: 17 | SrsRtmpClient* srs; 18 | StSocket* socket; 19 | int stream_id; 20 | public: 21 | StRtmpClient(); 22 | virtual ~StRtmpClient(); 23 | public: 24 | virtual int Dump(RtmpUrl* url); 25 | private: 26 | virtual int Connect(RtmpUrl* url); 27 | virtual int Handshake(); 28 | virtual int ConnectApp(RtmpUrl* url); 29 | virtual int CreateStream(); 30 | virtual int PlayStram(RtmpUrl* url); 31 | virtual int DumpAV(); 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/app/htl_app_rtmp_load.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <inttypes.h> 4 | #include <stdlib.h> 5 | 6 | #include <string> 7 | #include <sstream> 8 | using namespace std; 9 | 10 | #include <htl_core_error.hpp> 11 | #include <htl_core_log.hpp> 12 | #include <htl_app_rtmp_client.hpp> 13 | 14 | #include <htl_app_rtmp_load.hpp> 15 | 16 | StRtmpTask::StRtmpTask(){ 17 | } 18 | 19 | StRtmpTask::~StRtmpTask(){ 20 | } 21 | 22 | int StRtmpTask::Initialize(std::string http_url, double startup, double delay, double error, int count){ 23 | int ret = ERROR_SUCCESS; 24 | 25 | if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){ 26 | return ret; 27 | } 28 | 29 | return ret; 30 | } 31 | 32 | Uri* StRtmpTask::GetUri(){ 33 | return &url; 34 | } 35 | 36 | int StRtmpTask::ProcessTask(){ 37 | int ret = ERROR_SUCCESS; 38 | 39 | Trace("start to process RTMP task #%d, schema=%s, host=%s, port=%d, tcUrl=%s, stream=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d", 40 | GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetTcUrl(), url.GetStream(), startup_seconds, delay_seconds, error_seconds, count); 41 | 42 | StRtmpClient client; 43 | 44 | // if count is zero, infinity loop. 45 | for(int i = 0; count == 0 || i < count; i++){ 46 | statistic->OnTaskStart(GetId(), url.GetUrl()); 47 | 48 | if((ret = client.Dump(&url)) != ERROR_SUCCESS){ 49 | statistic->OnTaskError(GetId(), 0); 50 | 51 | Error("rtmp client dump url failed. ret=%d", ret); 52 | st_usleep((st_utime_t)(error_seconds * 1000 * 1000)); 53 | continue; 54 | } 55 | 56 | int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0); 57 | Trace("[RTMP] %s dump success, sleep %dms", url.GetUrl(), sleep_ms); 58 | st_usleep(sleep_ms * 1000); 59 | 60 | statistic->OnTaskEnd(GetId(), 0); 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/app/htl_app_rtmp_load.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_rtmp_load_hpp 2 | #define _htl_app_rtmp_load_hpp 3 | 4 | /* 5 | #include <htl_app_rtmp_load.hpp> 6 | */ 7 | #include <htl_app_task_base.hpp> 8 | 9 | // for rtmp task. 10 | class StRtmpTask : public StBaseTask 11 | { 12 | private: 13 | RtmpUrl url; 14 | public: 15 | StRtmpTask(); 16 | virtual ~StRtmpTask(); 17 | public: 18 | virtual int Initialize(std::string http_url, double startup, double delay, double error, int count); 19 | protected: 20 | virtual Uri* GetUri(); 21 | virtual int ProcessTask(); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/app/htl_app_rtmp_protocol.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_rtmp_protocol_hpp 2 | #define _htl_app_rtmp_protocol_hpp 3 | 4 | /* 5 | #include <htl_app_rtmp_protocol.hpp> 6 | */ 7 | 8 | #include <st.h> 9 | #include <vector> 10 | #include <map> 11 | 12 | /* 13 | The MIT License (MIT) 14 | 15 | Copyright (c) 2013 winlin 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy of 18 | this software and associated documentation files (the "Software"), to deal in 19 | the Software without restriction, including without limitation the rights to 20 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 21 | the Software, and to permit persons to whom the Software is furnished to do so, 22 | subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in all 25 | copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 29 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 30 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 31 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | class SrsProtocol; 35 | class ISrsMessage; 36 | class SrsCommonMessage; 37 | class SrsCreateStreamPacket; 38 | class SrsFMLEStartPacket; 39 | class SrsPublishPacket; 40 | class SrsSharedPtrMessage; 41 | class SrsOnMetaDataPacket; 42 | 43 | #if 1 44 | #define srs_verbose(msg, ...) (void)0 45 | #define srs_info(msg, ...) (void)0 46 | #define srs_trace(msg, ...) (void)0 47 | #define srs_warn(msg, ...) (void)0 48 | #define srs_error(msg, ...) (void)0 49 | #endif 50 | 51 | // donot use openssl, use simple handshake. 52 | #undef SRS_SSL 53 | 54 | #include <assert.h> 55 | #define srs_assert(expression) assert(expression) 56 | 57 | // current release version 58 | #define RTMP_SIG_SRS_VERSION "0.5.0" 59 | // server info. 60 | #define RTMP_SIG_SRS_KEY "srs" 61 | #define RTMP_SIG_SRS_ROLE "origin server" 62 | #define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(simple rtmp server)" 63 | #define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT 64 | #define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" 65 | #define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" 66 | #define RTMP_SIG_SRS_EMAIL "winterserver@126.com" 67 | #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" 68 | #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" 69 | #define RTMP_SIG_SRS_CONTRIBUTOR "winlin" 70 | 71 | // default vhost for rtmp 72 | #define RTMP_VHOST_DEFAULT "__defaultVhost__" 73 | 74 | #define SRS_LOCALHOST "127.0.0.1" 75 | #define RTMP_DEFAULT_PORT 1935 76 | #define RTMP_DEFAULT_PORTS "1935" 77 | 78 | #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" 79 | #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 80 | #define SRS_CONF_DEFAULT_HLS_WINDOW 60 81 | // in ms, for HLS aac sync time. 82 | #define SRS_CONF_DEFAULT_AAC_SYNC 100 83 | // in ms, for HLS aac flush the audio 84 | #define SRS_CONF_DEFAULT_AAC_DELAY 300 85 | 86 | #define ERROR_SUCCESS 0 87 | 88 | #define ERROR_ST_SET_EPOLL 100 89 | #ifndef ERROR_ST_INITIALIZE 90 | #define ERROR_ST_INITIALIZE 101 91 | #endif 92 | #define ERROR_ST_OPEN_SOCKET 102 93 | #define ERROR_ST_CREATE_LISTEN_THREAD 103 94 | #define ERROR_ST_CREATE_CYCLE_THREAD 104 95 | #define ERROR_ST_CREATE_FORWARD_THREAD 105 96 | #define ERROR_ST_CONNECT 106 97 | 98 | #define ERROR_SOCKET_CREATE 200 99 | #define ERROR_SOCKET_SETREUSE 201 100 | #define ERROR_SOCKET_BIND 202 101 | #define ERROR_SOCKET_LISTEN 203 102 | #define ERROR_SOCKET_CLOSED 204 103 | #define ERROR_SOCKET_GET_PEER_NAME 205 104 | #define ERROR_SOCKET_GET_PEER_IP 206 105 | #define ERROR_SOCKET_READ 207 106 | #define ERROR_SOCKET_READ_FULLY 208 107 | #define ERROR_SOCKET_WRITE 209 108 | #define ERROR_SOCKET_WAIT 210 109 | #define ERROR_SOCKET_TIMEOUT 211 110 | 111 | #define ERROR_RTMP_PLAIN_REQUIRED 300 112 | #define ERROR_RTMP_CHUNK_START 301 113 | #define ERROR_RTMP_MSG_INVLIAD_SIZE 302 114 | #define ERROR_RTMP_AMF0_DECODE 303 115 | #define ERROR_RTMP_AMF0_INVALID 304 116 | #define ERROR_RTMP_REQ_CONNECT 305 117 | #define ERROR_RTMP_REQ_TCURL 306 118 | #define ERROR_RTMP_MESSAGE_DECODE 307 119 | #define ERROR_RTMP_MESSAGE_ENCODE 308 120 | #define ERROR_RTMP_AMF0_ENCODE 309 121 | #define ERROR_RTMP_CHUNK_SIZE 310 122 | #define ERROR_RTMP_TRY_SIMPLE_HS 311 123 | #define ERROR_RTMP_CH_SCHEMA 312 124 | #define ERROR_RTMP_PACKET_SIZE 313 125 | #define ERROR_RTMP_VHOST_NOT_FOUND 314 126 | #define ERROR_RTMP_ACCESS_DENIED 315 127 | #define ERROR_RTMP_HANDSHAKE 316 128 | #define ERROR_RTMP_NO_REQUEST 317 129 | 130 | #define ERROR_SYSTEM_STREAM_INIT 400 131 | #define ERROR_SYSTEM_PACKET_INVALID 401 132 | #define ERROR_SYSTEM_CLIENT_INVALID 402 133 | #define ERROR_SYSTEM_ASSERT_FAILED 403 134 | #define ERROR_SYSTEM_SIZE_NEGATIVE 404 135 | #define ERROR_SYSTEM_CONFIG_INVALID 405 136 | #define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 137 | #define ERROR_SYSTEM_CONFIG_BLOCK_START 407 138 | #define ERROR_SYSTEM_CONFIG_BLOCK_END 408 139 | #define ERROR_SYSTEM_CONFIG_EOF 409 140 | #define ERROR_SYSTEM_STREAM_BUSY 410 141 | #define ERROR_SYSTEM_IP_INVALID 411 142 | #define ERROR_SYSTEM_CONFIG_TOO_LARGE 412 143 | #define ERROR_SYSTEM_FORWARD_LOOP 413 144 | #define ERROR_SYSTEM_WAITPID 414 145 | 146 | // see librtmp. 147 | // failed when open ssl create the dh 148 | #define ERROR_OpenSslCreateDH 500 149 | // failed when open ssl create the Private key. 150 | #define ERROR_OpenSslCreateP 501 151 | // when open ssl create G. 152 | #define ERROR_OpenSslCreateG 502 153 | // when open ssl parse P1024 154 | #define ERROR_OpenSslParseP1024 503 155 | // when open ssl set G 156 | #define ERROR_OpenSslSetG 504 157 | // when open ssl generate DHKeys 158 | #define ERROR_OpenSslGenerateDHKeys 505 159 | // when open ssl share key already computed. 160 | #define ERROR_OpenSslShareKeyComputed 506 161 | // when open ssl get shared key size. 162 | #define ERROR_OpenSslGetSharedKeySize 507 163 | // when open ssl get peer public key. 164 | #define ERROR_OpenSslGetPeerPublicKey 508 165 | // when open ssl compute shared key. 166 | #define ERROR_OpenSslComputeSharedKey 509 167 | // when open ssl is invalid DH state. 168 | #define ERROR_OpenSslInvalidDHState 510 169 | // when open ssl copy key 170 | #define ERROR_OpenSslCopyKey 511 171 | // when open ssl sha256 digest key invalid size. 172 | #define ERROR_OpenSslSha256DigestSize 512 173 | 174 | #define ERROR_HLS_METADATA 600 175 | #define ERROR_HLS_DECODE_ERROR 601 176 | #define ERROR_HLS_CREATE_DIR 602 177 | #define ERROR_HLS_OPEN_FAILED 603 178 | #define ERROR_HLS_WRITE_FAILED 604 179 | #define ERROR_HLS_AAC_FRAME_LENGTH 605 180 | #define ERROR_HLS_AVC_SAMPLE_SIZE 606 181 | 182 | #define ERROR_ENCODER_VCODEC 700 183 | #define ERROR_ENCODER_OUTPUT 701 184 | #define ERROR_ENCODER_ACHANNELS 702 185 | #define ERROR_ENCODER_ASAMPLE_RATE 703 186 | #define ERROR_ENCODER_ABITRATE 704 187 | #define ERROR_ENCODER_ACODEC 705 188 | #define ERROR_ENCODER_VPRESET 706 189 | #define ERROR_ENCODER_VPROFILE 707 190 | #define ERROR_ENCODER_VTHREADS 708 191 | #define ERROR_ENCODER_VHEIGHT 709 192 | #define ERROR_ENCODER_VWIDTH 710 193 | #define ERROR_ENCODER_VFPS 711 194 | #define ERROR_ENCODER_VBITRATE 712 195 | #define ERROR_ENCODER_FORK 713 196 | #define ERROR_ENCODER_LOOP 714 197 | #define ERROR_ENCODER_OPEN 715 198 | #define ERROR_ENCODER_DUP2 716 199 | 200 | // free the p and set to NULL. 201 | // p must be a T*. 202 | #define srs_freep(p) \ 203 | if (p) { \ 204 | delete p; \ 205 | p = NULL; \ 206 | } \ 207 | (void)0 208 | // free the p which represents a array 209 | #define srs_freepa(p) \ 210 | if (p) { \ 211 | delete[] p; \ 212 | p = NULL; \ 213 | } \ 214 | (void)0 215 | 216 | // compare 217 | #define srs_min(a, b) (((a) < (b))? (a) : (b)) 218 | #define srs_max(a, b) (((a) < (b))? (b) : (a)) 219 | 220 | class SrsSocket; 221 | 222 | /** 223 | * the buffer provices bytes cache for protocol. generally, 224 | * protocol recv data from socket, put into buffer, decode to RTMP message. 225 | * protocol encode RTMP message to bytes, put into buffer, send to socket. 226 | */ 227 | class SrsBuffer 228 | { 229 | private: 230 | std::vector<char> data; 231 | public: 232 | SrsBuffer(); 233 | virtual ~SrsBuffer(); 234 | public: 235 | virtual int size(); 236 | virtual char* bytes(); 237 | virtual void erase(int size); 238 | private: 239 | virtual void append(char* bytes, int size); 240 | public: 241 | virtual int ensure_buffer_bytes(SrsSocket* skt, int required_size); 242 | }; 243 | 244 | class SrsStream 245 | { 246 | private: 247 | char* p; 248 | char* pp; 249 | char* bytes; 250 | int size; 251 | public: 252 | SrsStream(); 253 | virtual ~SrsStream(); 254 | public: 255 | /** 256 | * initialize the stream from bytes. 257 | * @_bytes, must not be NULL, or return error. 258 | * @_size, must be positive, or return error. 259 | * @remark, stream never free the _bytes, user must free it. 260 | */ 261 | virtual int initialize(char* _bytes, int _size); 262 | /** 263 | * reset the position to beginning. 264 | */ 265 | virtual void reset(); 266 | /** 267 | * whether stream is empty. 268 | * if empty, never read or write. 269 | */ 270 | virtual bool empty(); 271 | /** 272 | * whether required size is ok. 273 | * @return true if stream can read/write specified required_size bytes. 274 | */ 275 | virtual bool require(int required_size); 276 | /** 277 | * to skip some size. 278 | * @size can be any value. positive to forward; nagetive to backward. 279 | */ 280 | virtual void skip(int size); 281 | /** 282 | * tell the current pos. 283 | */ 284 | virtual int pos(); 285 | /** 286 | * left size of bytes. 287 | */ 288 | virtual int left(); 289 | virtual char* current(); 290 | public: 291 | /** 292 | * get 1bytes char from stream. 293 | */ 294 | virtual int8_t read_1bytes(); 295 | /** 296 | * get 2bytes int from stream. 297 | */ 298 | virtual int16_t read_2bytes(); 299 | /** 300 | * get 3bytes int from stream. 301 | */ 302 | virtual int32_t read_3bytes(); 303 | /** 304 | * get 4bytes int from stream. 305 | */ 306 | virtual int32_t read_4bytes(); 307 | /** 308 | * get 8bytes int from stream. 309 | */ 310 | virtual int64_t read_8bytes(); 311 | /** 312 | * get string from stream, length specifies by param len. 313 | */ 314 | virtual std::string read_string(int len); 315 | public: 316 | /** 317 | * write 1bytes char to stream. 318 | */ 319 | virtual void write_1bytes(int8_t value); 320 | /** 321 | * write 2bytes int to stream. 322 | */ 323 | virtual void write_2bytes(int16_t value); 324 | /** 325 | * write 4bytes int to stream. 326 | */ 327 | virtual void write_4bytes(int32_t value); 328 | /** 329 | * write 8bytes int to stream. 330 | */ 331 | virtual void write_8bytes(int64_t value); 332 | /** 333 | * write string to stream 334 | */ 335 | virtual void write_string(std::string value); 336 | }; 337 | 338 | /** 339 | * the socket provides TCP socket over st, 340 | * that is, the sync socket mechanism. 341 | */ 342 | class SrsSocket 343 | { 344 | private: 345 | int64_t recv_timeout; 346 | int64_t send_timeout; 347 | int64_t recv_bytes; 348 | int64_t send_bytes; 349 | int64_t start_time_ms; 350 | st_netfd_t stfd; 351 | public: 352 | SrsSocket(st_netfd_t client_stfd); 353 | virtual ~SrsSocket(); 354 | public: 355 | virtual void set_recv_timeout(int64_t timeout_us); 356 | virtual int64_t get_recv_timeout(); 357 | virtual void set_send_timeout(int64_t timeout_us); 358 | virtual int64_t get_recv_bytes(); 359 | virtual int64_t get_send_bytes(); 360 | virtual int get_recv_kbps(); 361 | virtual int get_send_kbps(); 362 | public: 363 | virtual int read(const void* buf, size_t size, ssize_t* nread); 364 | virtual int read_fully(const void* buf, size_t size, ssize_t* nread); 365 | virtual int write(const void* buf, size_t size, ssize_t* nwrite); 366 | virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); 367 | }; 368 | 369 | class SrsSocket; 370 | class SrsComplexHandshake; 371 | 372 | /** 373 | * try complex handshake, if failed, fallback to simple handshake. 374 | */ 375 | class SrsSimpleHandshake 376 | { 377 | public: 378 | SrsSimpleHandshake(); 379 | virtual ~SrsSimpleHandshake(); 380 | public: 381 | /** 382 | * simple handshake. 383 | * @param complex_hs, try complex handshake first, 384 | * if failed, rollback to simple handshake. 385 | */ 386 | virtual int handshake_with_client(SrsSocket& skt, SrsComplexHandshake& complex_hs); 387 | virtual int handshake_with_server(SrsSocket& skt, SrsComplexHandshake& complex_hs); 388 | }; 389 | 390 | /** 391 | * rtmp complex handshake, 392 | * @see also crtmp(crtmpserver) or librtmp, 393 | * @see also: http://blog.csdn.net/win_lin/article/details/13006803 394 | */ 395 | class SrsComplexHandshake 396 | { 397 | public: 398 | SrsComplexHandshake(); 399 | virtual ~SrsComplexHandshake(); 400 | public: 401 | /** 402 | * complex hanshake. 403 | * @_c1, size of c1 must be 1536. 404 | * @remark, user must free the c1. 405 | * @return user must: 406 | * continue connect app if success, 407 | * try simple handshake if error is ERROR_RTMP_TRY_SIMPLE_HS, 408 | * otherwise, disconnect 409 | */ 410 | virtual int handshake_with_client(SrsSocket& skt, char* _c1); 411 | virtual int handshake_with_server(SrsSocket& skt); 412 | }; 413 | 414 | /** 415 | * auto free the instance in the current scope. 416 | */ 417 | #define SrsAutoFree(className, instance, is_array) \ 418 | __SrsAutoFree<className> _auto_free_##instance(&instance, is_array) 419 | 420 | template<class T> 421 | class __SrsAutoFree 422 | { 423 | private: 424 | T** ptr; 425 | bool is_array; 426 | public: 427 | /** 428 | * auto delete the ptr. 429 | * @is_array a bool value indicates whether the ptr is a array. 430 | */ 431 | __SrsAutoFree(T** _ptr, bool _is_array){ 432 | ptr = _ptr; 433 | is_array = _is_array; 434 | } 435 | 436 | virtual ~__SrsAutoFree(){ 437 | if (ptr == NULL || *ptr == NULL) { 438 | return; 439 | } 440 | 441 | if (is_array) { 442 | delete[] *ptr; 443 | } else { 444 | delete *ptr; 445 | } 446 | 447 | *ptr = NULL; 448 | } 449 | }; 450 | 451 | #include <string> 452 | #include <vector> 453 | 454 | class SrsStream; 455 | class SrsAmf0Object; 456 | 457 | /** 458 | * any amf0 value. 459 | * 2.1 Types Overview 460 | * value-type = number-type | boolean-type | string-type | object-type 461 | * | null-marker | undefined-marker | reference-type | ecma-array-type 462 | * | strict-array-type | date-type | long-string-type | xml-document-type 463 | * | typed-object-type 464 | */ 465 | struct SrsAmf0Any 466 | { 467 | char marker; 468 | 469 | SrsAmf0Any(); 470 | virtual ~SrsAmf0Any(); 471 | 472 | virtual bool is_string(); 473 | virtual bool is_boolean(); 474 | virtual bool is_number(); 475 | virtual bool is_null(); 476 | virtual bool is_undefined(); 477 | virtual bool is_object(); 478 | virtual bool is_object_eof(); 479 | virtual bool is_ecma_array(); 480 | }; 481 | 482 | /** 483 | * read amf0 string from stream. 484 | * 2.4 String Type 485 | * string-type = string-marker UTF-8 486 | * @return default value is empty string. 487 | */ 488 | struct SrsAmf0String : public SrsAmf0Any 489 | { 490 | std::string value; 491 | 492 | SrsAmf0String(const char* _value = NULL); 493 | virtual ~SrsAmf0String(); 494 | }; 495 | 496 | /** 497 | * read amf0 boolean from stream. 498 | * 2.4 String Type 499 | * boolean-type = boolean-marker U8 500 | * 0 is false, <> 0 is true 501 | * @return default value is false. 502 | */ 503 | struct SrsAmf0Boolean : public SrsAmf0Any 504 | { 505 | bool value; 506 | 507 | SrsAmf0Boolean(bool _value = false); 508 | virtual ~SrsAmf0Boolean(); 509 | }; 510 | 511 | /** 512 | * read amf0 number from stream. 513 | * 2.2 Number Type 514 | * number-type = number-marker DOUBLE 515 | * @return default value is 0. 516 | */ 517 | struct SrsAmf0Number : public SrsAmf0Any 518 | { 519 | double value; 520 | 521 | SrsAmf0Number(double _value = 0.0); 522 | virtual ~SrsAmf0Number(); 523 | }; 524 | 525 | /** 526 | * read amf0 null from stream. 527 | * 2.7 null Type 528 | * null-type = null-marker 529 | */ 530 | struct SrsAmf0Null : public SrsAmf0Any 531 | { 532 | SrsAmf0Null(); 533 | virtual ~SrsAmf0Null(); 534 | }; 535 | 536 | /** 537 | * read amf0 undefined from stream. 538 | * 2.8 undefined Type 539 | * undefined-type = undefined-marker 540 | */ 541 | struct SrsAmf0Undefined : public SrsAmf0Any 542 | { 543 | SrsAmf0Undefined(); 544 | virtual ~SrsAmf0Undefined(); 545 | }; 546 | 547 | /** 548 | * 2.11 Object End Type 549 | * object-end-type = UTF-8-empty object-end-marker 550 | * 0x00 0x00 0x09 551 | */ 552 | struct SrsAmf0ObjectEOF : public SrsAmf0Any 553 | { 554 | int16_t utf8_empty; 555 | 556 | SrsAmf0ObjectEOF(); 557 | virtual ~SrsAmf0ObjectEOF(); 558 | }; 559 | 560 | /** 561 | * to ensure in inserted order. 562 | * for the FMLE will crash when AMF0Object is not ordered by inserted, 563 | * if ordered in map, the string compare order, the FMLE will creash when 564 | * get the response of connect app. 565 | */ 566 | struct SrsUnSortedHashtable 567 | { 568 | private: 569 | typedef std::pair<std::string, SrsAmf0Any*> SrsObjectPropertyType; 570 | std::vector<SrsObjectPropertyType> properties; 571 | public: 572 | SrsUnSortedHashtable(); 573 | virtual ~SrsUnSortedHashtable(); 574 | 575 | virtual int size(); 576 | virtual void clear(); 577 | virtual std::string key_at(int index); 578 | virtual SrsAmf0Any* value_at(int index); 579 | virtual void set(std::string key, SrsAmf0Any* value); 580 | 581 | virtual SrsAmf0Any* get_property(std::string name); 582 | virtual SrsAmf0Any* ensure_property_string(std::string name); 583 | virtual SrsAmf0Any* ensure_property_number(std::string name); 584 | }; 585 | 586 | /** 587 | * 2.5 Object Type 588 | * anonymous-object-type = object-marker *(object-property) 589 | * object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) 590 | */ 591 | struct SrsAmf0Object : public SrsAmf0Any 592 | { 593 | private: 594 | SrsUnSortedHashtable properties; 595 | public: 596 | SrsAmf0ObjectEOF eof; 597 | 598 | SrsAmf0Object(); 599 | virtual ~SrsAmf0Object(); 600 | 601 | virtual int size(); 602 | virtual std::string key_at(int index); 603 | virtual SrsAmf0Any* value_at(int index); 604 | virtual void set(std::string key, SrsAmf0Any* value); 605 | 606 | virtual SrsAmf0Any* get_property(std::string name); 607 | virtual SrsAmf0Any* ensure_property_string(std::string name); 608 | virtual SrsAmf0Any* ensure_property_number(std::string name); 609 | }; 610 | 611 | /** 612 | * 2.10 ECMA Array Type 613 | * ecma-array-type = associative-count *(object-property) 614 | * associative-count = U32 615 | * object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) 616 | */ 617 | struct SrsASrsAmf0EcmaArray : public SrsAmf0Any 618 | { 619 | private: 620 | SrsUnSortedHashtable properties; 621 | public: 622 | int32_t count; 623 | SrsAmf0ObjectEOF eof; 624 | 625 | SrsASrsAmf0EcmaArray(); 626 | virtual ~SrsASrsAmf0EcmaArray(); 627 | 628 | virtual int size(); 629 | virtual void clear(); 630 | virtual std::string key_at(int index); 631 | virtual SrsAmf0Any* value_at(int index); 632 | virtual void set(std::string key, SrsAmf0Any* value); 633 | 634 | virtual SrsAmf0Any* get_property(std::string name); 635 | virtual SrsAmf0Any* ensure_property_string(std::string name); 636 | }; 637 | 638 | /** 639 | * read amf0 utf8 string from stream. 640 | * 1.3.1 Strings and UTF-8 641 | * UTF-8 = U16 *(UTF8-char) 642 | * UTF8-char = UTF8-1 | UTF8-2 | UTF8-3 | UTF8-4 643 | * UTF8-1 = %x00-7F 644 | * @remark only support UTF8-1 char. 645 | */ 646 | extern int srs_amf0_read_utf8(SrsStream* stream, std::string& value); 647 | extern int srs_amf0_write_utf8(SrsStream* stream, std::string value); 648 | 649 | /** 650 | * read amf0 string from stream. 651 | * 2.4 String Type 652 | * string-type = string-marker UTF-8 653 | */ 654 | extern int srs_amf0_read_string(SrsStream* stream, std::string& value); 655 | extern int srs_amf0_write_string(SrsStream* stream, std::string value); 656 | 657 | /** 658 | * read amf0 boolean from stream. 659 | * 2.4 String Type 660 | * boolean-type = boolean-marker U8 661 | * 0 is false, <> 0 is true 662 | */ 663 | extern int srs_amf0_read_boolean(SrsStream* stream, bool& value); 664 | extern int srs_amf0_write_boolean(SrsStream* stream, bool value); 665 | 666 | /** 667 | * read amf0 number from stream. 668 | * 2.2 Number Type 669 | * number-type = number-marker DOUBLE 670 | */ 671 | extern int srs_amf0_read_number(SrsStream* stream, double& value); 672 | extern int srs_amf0_write_number(SrsStream* stream, double value); 673 | 674 | /** 675 | * read amf0 null from stream. 676 | * 2.7 null Type 677 | * null-type = null-marker 678 | */ 679 | extern int srs_amf0_read_null(SrsStream* stream); 680 | extern int srs_amf0_write_null(SrsStream* stream); 681 | 682 | /** 683 | * read amf0 undefined from stream. 684 | * 2.8 undefined Type 685 | * undefined-type = undefined-marker 686 | */ 687 | extern int srs_amf0_read_undefined(SrsStream* stream); 688 | extern int srs_amf0_write_undefined(SrsStream* stream); 689 | 690 | extern int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value); 691 | 692 | /** 693 | * read amf0 object from stream. 694 | * 2.5 Object Type 695 | * anonymous-object-type = object-marker *(object-property) 696 | * object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) 697 | */ 698 | extern int srs_amf0_read_object(SrsStream* stream, SrsAmf0Object*& value); 699 | extern int srs_amf0_write_object(SrsStream* stream, SrsAmf0Object* value); 700 | 701 | /** 702 | * read amf0 object from stream. 703 | * 2.10 ECMA Array Type 704 | * ecma-array-type = associative-count *(object-property) 705 | * associative-count = U32 706 | * object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) 707 | */ 708 | extern int srs_amf0_read_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray*& value); 709 | extern int srs_amf0_write_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray* value); 710 | 711 | /** 712 | * get amf0 objects size. 713 | */ 714 | extern int srs_amf0_get_utf8_size(std::string value); 715 | extern int srs_amf0_get_string_size(std::string value); 716 | extern int srs_amf0_get_number_size(); 717 | extern int srs_amf0_get_null_size(); 718 | extern int srs_amf0_get_undefined_size(); 719 | extern int srs_amf0_get_boolean_size(); 720 | extern int srs_amf0_get_object_size(SrsAmf0Object* obj); 721 | extern int srs_amf0_get_ecma_array_size(SrsASrsAmf0EcmaArray* arr); 722 | 723 | /** 724 | * convert the any to specified object. 725 | * @return T*, the converted object. never NULL. 726 | * @remark, user must ensure the current object type, 727 | * or the covert will cause assert failed. 728 | */ 729 | template<class T> 730 | T* srs_amf0_convert(SrsAmf0Any* any) 731 | { 732 | T* p = dynamic_cast<T*>(any); 733 | srs_assert(p != NULL); 734 | return p; 735 | } 736 | 737 | /** 738 | * the original request from client. 739 | */ 740 | struct SrsRequest 741 | { 742 | /** 743 | * tcUrl: rtmp://request_vhost:port/app/stream 744 | * support pass vhost in query string, such as: 745 | * rtmp://ip:port/app?vhost=request_vhost/stream 746 | * rtmp://ip:port/app...vhost...request_vhost/stream 747 | */ 748 | std::string tcUrl; 749 | std::string pageUrl; 750 | std::string swfUrl; 751 | double objectEncoding; 752 | 753 | std::string schema; 754 | std::string vhost; 755 | std::string port; 756 | std::string app; 757 | std::string stream; 758 | 759 | SrsRequest(); 760 | virtual ~SrsRequest(); 761 | #if 0 //disable the server-side behavior. 762 | /** 763 | * disconvery vhost/app from tcUrl. 764 | */ 765 | virtual int discovery_app(); 766 | #endif 767 | virtual std::string get_stream_url(); 768 | virtual void strip(); 769 | private: 770 | std::string& trim(std::string& str, std::string chs); 771 | }; 772 | 773 | /** 774 | * the response to client. 775 | */ 776 | struct SrsResponse 777 | { 778 | int stream_id; 779 | 780 | SrsResponse(); 781 | virtual ~SrsResponse(); 782 | }; 783 | 784 | /** 785 | * the rtmp client type. 786 | */ 787 | enum SrsClientType 788 | { 789 | SrsClientUnknown, 790 | SrsClientPlay, 791 | SrsClientFMLEPublish, 792 | SrsClientFlashPublish, 793 | }; 794 | 795 | class SrsSocket; 796 | class SrsBuffer; 797 | class SrsPacket; 798 | class SrsStream; 799 | class SrsCommonMessage; 800 | class SrsChunkStream; 801 | class SrsAmf0Object; 802 | class SrsAmf0Null; 803 | class SrsAmf0Undefined; 804 | class ISrsMessage; 805 | 806 | // convert class name to string. 807 | #define CLASS_NAME_STRING(className) #className 808 | 809 | /** 810 | * max rtmp header size: 811 | * 1bytes basic header, 812 | * 11bytes message header, 813 | * 4bytes timestamp header, 814 | * that is, 1+11+4=16bytes. 815 | */ 816 | #define RTMP_MAX_FMT0_HEADER_SIZE 16 817 | /** 818 | * max rtmp header size: 819 | * 1bytes basic header, 820 | * 4bytes timestamp header, 821 | * that is, 1+4=5bytes. 822 | */ 823 | #define RTMP_MAX_FMT3_HEADER_SIZE 5 824 | 825 | /** 826 | * the protocol provides the rtmp-message-protocol services, 827 | * to recv RTMP message from RTMP chunk stream, 828 | * and to send out RTMP message over RTMP chunk stream. 829 | */ 830 | class SrsProtocol 831 | { 832 | private: 833 | struct AckWindowSize 834 | { 835 | int ack_window_size; 836 | int64_t acked_size; 837 | 838 | AckWindowSize(); 839 | }; 840 | // peer in/out 841 | private: 842 | st_netfd_t stfd; 843 | SrsSocket* skt; 844 | char* pp; 845 | /** 846 | * requests sent out, used to build the response. 847 | * key: transactionId 848 | * value: the request command name 849 | */ 850 | std::map<double, std::string> requests; 851 | // peer in 852 | private: 853 | std::map<int, SrsChunkStream*> chunk_streams; 854 | SrsBuffer* buffer; 855 | int32_t in_chunk_size; 856 | AckWindowSize in_ack_size; 857 | // peer out 858 | private: 859 | char out_header_fmt0[RTMP_MAX_FMT0_HEADER_SIZE]; 860 | char out_header_fmt3[RTMP_MAX_FMT3_HEADER_SIZE]; 861 | int32_t out_chunk_size; 862 | public: 863 | SrsProtocol(st_netfd_t client_stfd); 864 | virtual ~SrsProtocol(); 865 | public: 866 | std::string get_request_name(double transcationId); 867 | /** 868 | * set the timeout in us. 869 | * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. 870 | */ 871 | virtual void set_recv_timeout(int64_t timeout_us); 872 | virtual int64_t get_recv_timeout(); 873 | virtual void set_send_timeout(int64_t timeout_us); 874 | virtual int64_t get_recv_bytes(); 875 | virtual int64_t get_send_bytes(); 876 | virtual int get_recv_kbps(); 877 | virtual int get_send_kbps(); 878 | /** 879 | * recv a message with raw/undecoded payload from peer. 880 | * the payload is not decoded, use srs_rtmp_expect_message<T> if requires 881 | * specifies message. 882 | * @pmsg, user must free it. NULL if not success. 883 | * @remark, only when success, user can use and must free the pmsg. 884 | */ 885 | virtual int recv_message(SrsCommonMessage** pmsg); 886 | /** 887 | * send out message with encoded payload to peer. 888 | * use the message encode method to encode to payload, 889 | * then sendout over socket. 890 | * @msg this method will free it whatever return value. 891 | */ 892 | virtual int send_message(ISrsMessage* msg); 893 | private: 894 | /** 895 | * when recv message, update the context. 896 | */ 897 | virtual int on_recv_message(SrsCommonMessage* msg); 898 | virtual int response_acknowledgement_message(); 899 | virtual int response_ping_message(int32_t timestamp); 900 | /** 901 | * when message sentout, update the context. 902 | */ 903 | virtual int on_send_message(ISrsMessage* msg); 904 | /** 905 | * try to recv interlaced message from peer, 906 | * return error if error occur and nerver set the pmsg, 907 | * return success and pmsg set to NULL if no entire message got, 908 | * return success and pmsg set to entire message if got one. 909 | */ 910 | virtual int recv_interlaced_message(SrsCommonMessage** pmsg); 911 | /** 912 | * read the chunk basic header(fmt, cid) from chunk stream. 913 | * user can discovery a SrsChunkStream by cid. 914 | * @bh_size return the chunk basic header size, to remove the used bytes when finished. 915 | */ 916 | virtual int read_basic_header(char& fmt, int& cid, int& bh_size); 917 | /** 918 | * read the chunk message header(timestamp, payload_length, message_type, stream_id) 919 | * from chunk stream and save to SrsChunkStream. 920 | * @mh_size return the chunk message header size, to remove the used bytes when finished. 921 | */ 922 | virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); 923 | /** 924 | * read the chunk payload, remove the used bytes in buffer, 925 | * if got entire message, set the pmsg. 926 | * @payload_size read size in this roundtrip, generally a chunk size or left message size. 927 | */ 928 | virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg); 929 | }; 930 | 931 | /** 932 | * 4.1. Message Header 933 | */ 934 | struct SrsMessageHeader 935 | { 936 | /** 937 | * One byte field to represent the message type. A range of type IDs 938 | * (1-7) are reserved for protocol control messages. 939 | */ 940 | int8_t message_type; 941 | /** 942 | * Three-byte field that represents the size of the payload in bytes. 943 | * It is set in big-endian format. 944 | */ 945 | int32_t payload_length; 946 | /** 947 | * Three-byte field that contains a timestamp delta of the message. 948 | * The 4 bytes are packed in the big-endian order. 949 | * @remark, only used for decoding message from chunk stream. 950 | */ 951 | int32_t timestamp_delta; 952 | /** 953 | * Three-byte field that identifies the stream of the message. These 954 | * bytes are set in big-endian format. 955 | */ 956 | int32_t stream_id; 957 | 958 | /** 959 | * Four-byte field that contains a timestamp of the message. 960 | * The 4 bytes are packed in the big-endian order. 961 | * @remark, used as calc timestamp when decode and encode time. 962 | */ 963 | u_int32_t timestamp; 964 | 965 | SrsMessageHeader(); 966 | virtual ~SrsMessageHeader(); 967 | 968 | bool is_audio(); 969 | bool is_video(); 970 | bool is_amf0_command(); 971 | bool is_amf0_data(); 972 | bool is_amf3_command(); 973 | bool is_amf3_data(); 974 | bool is_window_ackledgement_size(); 975 | bool is_set_chunk_size(); 976 | bool is_user_control_message(); 977 | }; 978 | 979 | /** 980 | * incoming chunk stream maybe interlaced, 981 | * use the chunk stream to cache the input RTMP chunk streams. 982 | */ 983 | class SrsChunkStream 984 | { 985 | public: 986 | /** 987 | * represents the basic header fmt, 988 | * which used to identify the variant message header type. 989 | */ 990 | char fmt; 991 | /** 992 | * represents the basic header cid, 993 | * which is the chunk stream id. 994 | */ 995 | int cid; 996 | /** 997 | * cached message header 998 | */ 999 | SrsMessageHeader header; 1000 | /** 1001 | * whether the chunk message header has extended timestamp. 1002 | */ 1003 | bool extended_timestamp; 1004 | /** 1005 | * partially read message. 1006 | */ 1007 | SrsCommonMessage* msg; 1008 | /** 1009 | * decoded msg count, to identify whether the chunk stream is fresh. 1010 | */ 1011 | int64_t msg_count; 1012 | public: 1013 | SrsChunkStream(int _cid); 1014 | virtual ~SrsChunkStream(); 1015 | }; 1016 | 1017 | /** 1018 | * message to output. 1019 | */ 1020 | class ISrsMessage 1021 | { 1022 | // 4.1. Message Header 1023 | public: 1024 | SrsMessageHeader header; 1025 | // 4.2. Message Payload 1026 | public: 1027 | /** 1028 | * The other part which is the payload is the actual data that is 1029 | * contained in the message. For example, it could be some audio samples 1030 | * or compressed video data. The payload format and interpretation are 1031 | * beyond the scope of this document. 1032 | */ 1033 | int32_t size; 1034 | int8_t* payload; 1035 | public: 1036 | ISrsMessage(); 1037 | virtual ~ISrsMessage(); 1038 | public: 1039 | /** 1040 | * whether message canbe decoded. 1041 | * only update the context when message canbe decoded. 1042 | */ 1043 | virtual bool can_decode() = 0; 1044 | /** 1045 | * encode functions. 1046 | */ 1047 | public: 1048 | /** 1049 | * get the perfered cid(chunk stream id) which sendout over. 1050 | */ 1051 | virtual int get_perfer_cid() = 0; 1052 | /** 1053 | * encode the packet to message payload bytes. 1054 | * @remark there exists empty packet, so maybe the payload is NULL. 1055 | */ 1056 | virtual int encode_packet() = 0; 1057 | }; 1058 | 1059 | /** 1060 | * common RTMP message defines in rtmp.part2.Message-Formats.pdf. 1061 | * cannbe parse and decode. 1062 | */ 1063 | class SrsCommonMessage : public ISrsMessage 1064 | { 1065 | private: 1066 | typedef ISrsMessage super; 1067 | // decoded message payload. 1068 | private: 1069 | SrsStream* stream; 1070 | SrsPacket* packet; 1071 | public: 1072 | SrsCommonMessage(); 1073 | virtual ~SrsCommonMessage(); 1074 | public: 1075 | virtual bool can_decode(); 1076 | /** 1077 | * decode functions. 1078 | */ 1079 | public: 1080 | /** 1081 | * decode packet from message payload. 1082 | */ 1083 | // TODO: use protocol to decode it. 1084 | virtual int decode_packet(SrsProtocol* protocol); 1085 | /** 1086 | * get the decoded packet which decoded by decode_packet(). 1087 | * @remark, user never free the pkt, the message will auto free it. 1088 | */ 1089 | virtual SrsPacket* get_packet(); 1090 | /** 1091 | * encode functions. 1092 | */ 1093 | public: 1094 | /** 1095 | * get the perfered cid(chunk stream id) which sendout over. 1096 | */ 1097 | virtual int get_perfer_cid(); 1098 | /** 1099 | * set the encoded packet to encode_packet() to payload. 1100 | * @stream_id, the id of stream which is created by createStream. 1101 | * @remark, user never free the pkt, the message will auto free it. 1102 | */ 1103 | // TODO: refine the send methods. 1104 | virtual void set_packet(SrsPacket* pkt, int stream_id); 1105 | /** 1106 | * encode the packet to message payload bytes. 1107 | * @remark there exists empty packet, so maybe the payload is NULL. 1108 | */ 1109 | virtual int encode_packet(); 1110 | }; 1111 | 1112 | /** 1113 | * shared ptr message. 1114 | * for audio/video/data message that need less memory copy. 1115 | * and only for output. 1116 | */ 1117 | class SrsSharedPtrMessage : public ISrsMessage 1118 | { 1119 | private: 1120 | typedef ISrsMessage super; 1121 | private: 1122 | struct SrsSharedPtr 1123 | { 1124 | char* payload; 1125 | int size; 1126 | int perfer_cid; 1127 | int shared_count; 1128 | 1129 | SrsSharedPtr(); 1130 | virtual ~SrsSharedPtr(); 1131 | }; 1132 | SrsSharedPtr* ptr; 1133 | public: 1134 | SrsSharedPtrMessage(); 1135 | virtual ~SrsSharedPtrMessage(); 1136 | public: 1137 | virtual bool can_decode(); 1138 | public: 1139 | /** 1140 | * set the shared payload. 1141 | * we will detach the payload of source, 1142 | * so ensure donot use it before. 1143 | */ 1144 | virtual int initialize(SrsCommonMessage* source); 1145 | /** 1146 | * set the shared payload. 1147 | * we will use the payload, donot use the payload of source. 1148 | */ 1149 | virtual int initialize(SrsCommonMessage* source, char* payload, int size); 1150 | virtual SrsSharedPtrMessage* copy(); 1151 | public: 1152 | /** 1153 | * get the perfered cid(chunk stream id) which sendout over. 1154 | */ 1155 | virtual int get_perfer_cid(); 1156 | /** 1157 | * ignored. 1158 | * for shared message, nothing should be done. 1159 | * use initialize() to set the data. 1160 | */ 1161 | virtual int encode_packet(); 1162 | }; 1163 | 1164 | /** 1165 | * the decoded message payload. 1166 | * @remark we seperate the packet from message, 1167 | * for the packet focus on logic and domain data, 1168 | * the message bind to the protocol and focus on protocol, such as header. 1169 | * we can merge the message and packet, using OOAD hierachy, packet extends from message, 1170 | * it's better for me to use components -- the message use the packet as payload. 1171 | */ 1172 | class SrsPacket 1173 | { 1174 | protected: 1175 | /** 1176 | * subpacket must override to provide the right class name. 1177 | */ 1178 | virtual const char* get_class_name() 1179 | { 1180 | return CLASS_NAME_STRING(SrsPacket); 1181 | } 1182 | public: 1183 | SrsPacket(); 1184 | virtual ~SrsPacket(); 1185 | /** 1186 | * decode functions. 1187 | */ 1188 | public: 1189 | /** 1190 | * subpacket must override to decode packet from stream. 1191 | * @remark never invoke the super.decode, it always failed. 1192 | */ 1193 | virtual int decode(SrsStream* stream); 1194 | /** 1195 | * encode functions. 1196 | */ 1197 | public: 1198 | virtual int get_perfer_cid(); 1199 | virtual int get_payload_length(); 1200 | public: 1201 | /** 1202 | * subpacket must override to provide the right message type. 1203 | */ 1204 | virtual int get_message_type(); 1205 | /** 1206 | * the subpacket can override this encode, 1207 | * for example, video and audio will directly set the payload withou memory copy, 1208 | * other packet which need to serialize/encode to bytes by override the 1209 | * get_size and encode_packet. 1210 | */ 1211 | virtual int encode(int& size, char*& payload); 1212 | protected: 1213 | /** 1214 | * subpacket can override to calc the packet size. 1215 | */ 1216 | virtual int get_size(); 1217 | /** 1218 | * subpacket can override to encode the payload to stream. 1219 | * @remark never invoke the super.encode_packet, it always failed. 1220 | */ 1221 | virtual int encode_packet(SrsStream* stream); 1222 | }; 1223 | 1224 | /** 1225 | * 4.1.1. connect 1226 | * The client sends the connect command to the server to request 1227 | * connection to a server application instance. 1228 | */ 1229 | class SrsConnectAppPacket : public SrsPacket 1230 | { 1231 | private: 1232 | typedef SrsPacket super; 1233 | protected: 1234 | virtual const char* get_class_name() 1235 | { 1236 | return CLASS_NAME_STRING(SrsConnectAppPacket); 1237 | } 1238 | public: 1239 | std::string command_name; 1240 | double transaction_id; 1241 | SrsAmf0Object* command_object; 1242 | public: 1243 | SrsConnectAppPacket(); 1244 | virtual ~SrsConnectAppPacket(); 1245 | public: 1246 | virtual int decode(SrsStream* stream); 1247 | public: 1248 | virtual int get_perfer_cid(); 1249 | public: 1250 | virtual int get_message_type(); 1251 | protected: 1252 | virtual int get_size(); 1253 | virtual int encode_packet(SrsStream* stream); 1254 | }; 1255 | /** 1256 | * response for SrsConnectAppPacket. 1257 | */ 1258 | class SrsConnectAppResPacket : public SrsPacket 1259 | { 1260 | private: 1261 | typedef SrsPacket super; 1262 | protected: 1263 | virtual const char* get_class_name() 1264 | { 1265 | return CLASS_NAME_STRING(SrsConnectAppResPacket); 1266 | } 1267 | public: 1268 | std::string command_name; 1269 | double transaction_id; 1270 | SrsAmf0Object* props; 1271 | SrsAmf0Object* info; 1272 | public: 1273 | SrsConnectAppResPacket(); 1274 | virtual ~SrsConnectAppResPacket(); 1275 | public: 1276 | virtual int decode(SrsStream* stream); 1277 | public: 1278 | virtual int get_perfer_cid(); 1279 | public: 1280 | virtual int get_message_type(); 1281 | protected: 1282 | virtual int get_size(); 1283 | virtual int encode_packet(SrsStream* stream); 1284 | }; 1285 | 1286 | /** 1287 | * 4.1.3. createStream 1288 | * The client sends this command to the server to create a logical 1289 | * channel for message communication The publishing of audio, video, and 1290 | * metadata is carried out over stream channel created using the 1291 | * createStream command. 1292 | */ 1293 | class SrsCreateStreamPacket : public SrsPacket 1294 | { 1295 | private: 1296 | typedef SrsPacket super; 1297 | protected: 1298 | virtual const char* get_class_name() 1299 | { 1300 | return CLASS_NAME_STRING(SrsCreateStreamPacket); 1301 | } 1302 | public: 1303 | std::string command_name; 1304 | double transaction_id; 1305 | SrsAmf0Null* command_object; 1306 | public: 1307 | SrsCreateStreamPacket(); 1308 | virtual ~SrsCreateStreamPacket(); 1309 | public: 1310 | virtual int decode(SrsStream* stream); 1311 | public: 1312 | virtual int get_perfer_cid(); 1313 | public: 1314 | virtual int get_message_type(); 1315 | protected: 1316 | virtual int get_size(); 1317 | virtual int encode_packet(SrsStream* stream); 1318 | }; 1319 | /** 1320 | * response for SrsCreateStreamPacket. 1321 | */ 1322 | class SrsCreateStreamResPacket : public SrsPacket 1323 | { 1324 | private: 1325 | typedef SrsPacket super; 1326 | protected: 1327 | virtual const char* get_class_name() 1328 | { 1329 | return CLASS_NAME_STRING(SrsCreateStreamResPacket); 1330 | } 1331 | public: 1332 | std::string command_name; 1333 | double transaction_id; 1334 | SrsAmf0Null* command_object; 1335 | double stream_id; 1336 | public: 1337 | SrsCreateStreamResPacket(double _transaction_id, double _stream_id); 1338 | virtual ~SrsCreateStreamResPacket(); 1339 | public: 1340 | virtual int decode(SrsStream* stream); 1341 | public: 1342 | virtual int get_perfer_cid(); 1343 | public: 1344 | virtual int get_message_type(); 1345 | protected: 1346 | virtual int get_size(); 1347 | virtual int encode_packet(SrsStream* stream); 1348 | }; 1349 | 1350 | /** 1351 | * FMLE start publish: ReleaseStream/PublishStream 1352 | */ 1353 | class SrsFMLEStartPacket : public SrsPacket 1354 | { 1355 | private: 1356 | typedef SrsPacket super; 1357 | protected: 1358 | virtual const char* get_class_name() 1359 | { 1360 | return CLASS_NAME_STRING(SrsFMLEStartPacket); 1361 | } 1362 | public: 1363 | std::string command_name; 1364 | double transaction_id; 1365 | SrsAmf0Null* command_object; 1366 | std::string stream_name; 1367 | public: 1368 | SrsFMLEStartPacket(); 1369 | virtual ~SrsFMLEStartPacket(); 1370 | public: 1371 | virtual int decode(SrsStream* stream); 1372 | }; 1373 | /** 1374 | * response for SrsFMLEStartPacket. 1375 | */ 1376 | class SrsFMLEStartResPacket : public SrsPacket 1377 | { 1378 | private: 1379 | typedef SrsPacket super; 1380 | protected: 1381 | virtual const char* get_class_name() 1382 | { 1383 | return CLASS_NAME_STRING(SrsFMLEStartResPacket); 1384 | } 1385 | public: 1386 | std::string command_name; 1387 | double transaction_id; 1388 | SrsAmf0Null* command_object; 1389 | SrsAmf0Undefined* args; 1390 | public: 1391 | SrsFMLEStartResPacket(double _transaction_id); 1392 | virtual ~SrsFMLEStartResPacket(); 1393 | public: 1394 | virtual int get_perfer_cid(); 1395 | public: 1396 | virtual int get_message_type(); 1397 | protected: 1398 | virtual int get_size(); 1399 | virtual int encode_packet(SrsStream* stream); 1400 | }; 1401 | 1402 | /** 1403 | * FMLE/flash publish 1404 | * 4.2.6. Publish 1405 | * The client sends the publish command to publish a named stream to the 1406 | * server. Using this name, any client can play this stream and receive 1407 | * the published audio, video, and data messages. 1408 | */ 1409 | class SrsPublishPacket : public SrsPacket 1410 | { 1411 | private: 1412 | typedef SrsPacket super; 1413 | protected: 1414 | virtual const char* get_class_name() 1415 | { 1416 | return CLASS_NAME_STRING(SrsPublishPacket); 1417 | } 1418 | public: 1419 | std::string command_name; 1420 | double transaction_id; 1421 | SrsAmf0Null* command_object; 1422 | std::string stream_name; 1423 | // optional, default to live. 1424 | std::string type; 1425 | public: 1426 | SrsPublishPacket(); 1427 | virtual ~SrsPublishPacket(); 1428 | public: 1429 | virtual int decode(SrsStream* stream); 1430 | public: 1431 | virtual int get_perfer_cid(); 1432 | public: 1433 | virtual int get_message_type(); 1434 | protected: 1435 | virtual int get_size(); 1436 | virtual int encode_packet(SrsStream* stream); 1437 | }; 1438 | 1439 | /** 1440 | * 4.2.8. pause 1441 | * The client sends the pause command to tell the server to pause or 1442 | * start playing. 1443 | */ 1444 | class SrsPausePacket : public SrsPacket 1445 | { 1446 | private: 1447 | typedef SrsPacket super; 1448 | protected: 1449 | virtual const char* get_class_name() 1450 | { 1451 | return CLASS_NAME_STRING(SrsPausePacket); 1452 | } 1453 | public: 1454 | std::string command_name; 1455 | double transaction_id; 1456 | SrsAmf0Null* command_object; 1457 | bool is_pause; 1458 | double time_ms; 1459 | public: 1460 | SrsPausePacket(); 1461 | virtual ~SrsPausePacket(); 1462 | public: 1463 | virtual int decode(SrsStream* stream); 1464 | }; 1465 | 1466 | /** 1467 | * 4.2.1. play 1468 | * The client sends this command to the server to play a stream. 1469 | */ 1470 | class SrsPlayPacket : public SrsPacket 1471 | { 1472 | private: 1473 | typedef SrsPacket super; 1474 | protected: 1475 | virtual const char* get_class_name() 1476 | { 1477 | return CLASS_NAME_STRING(SrsPlayPacket); 1478 | } 1479 | public: 1480 | std::string command_name; 1481 | double transaction_id; 1482 | SrsAmf0Null* command_object; 1483 | std::string stream_name; 1484 | double start; 1485 | double duration; 1486 | bool reset; 1487 | public: 1488 | SrsPlayPacket(); 1489 | virtual ~SrsPlayPacket(); 1490 | public: 1491 | virtual int decode(SrsStream* stream); 1492 | public: 1493 | virtual int get_perfer_cid(); 1494 | public: 1495 | virtual int get_message_type(); 1496 | protected: 1497 | virtual int get_size(); 1498 | virtual int encode_packet(SrsStream* stream); 1499 | }; 1500 | /** 1501 | * response for SrsPlayPacket. 1502 | * @remark, user must set the stream_id in header. 1503 | */ 1504 | class SrsPlayResPacket : public SrsPacket 1505 | { 1506 | private: 1507 | typedef SrsPacket super; 1508 | protected: 1509 | virtual const char* get_class_name() 1510 | { 1511 | return CLASS_NAME_STRING(SrsPlayResPacket); 1512 | } 1513 | public: 1514 | std::string command_name; 1515 | double transaction_id; 1516 | SrsAmf0Null* command_object; 1517 | SrsAmf0Object* desc; 1518 | public: 1519 | SrsPlayResPacket(); 1520 | virtual ~SrsPlayResPacket(); 1521 | public: 1522 | virtual int get_perfer_cid(); 1523 | public: 1524 | virtual int get_message_type(); 1525 | protected: 1526 | virtual int get_size(); 1527 | virtual int encode_packet(SrsStream* stream); 1528 | }; 1529 | 1530 | /** 1531 | * when bandwidth test done, notice client. 1532 | */ 1533 | class SrsOnBWDonePacket : public SrsPacket 1534 | { 1535 | private: 1536 | typedef SrsPacket super; 1537 | protected: 1538 | virtual const char* get_class_name() 1539 | { 1540 | return CLASS_NAME_STRING(SrsOnBWDonePacket); 1541 | } 1542 | public: 1543 | std::string command_name; 1544 | double transaction_id; 1545 | SrsAmf0Null* args; 1546 | public: 1547 | SrsOnBWDonePacket(); 1548 | virtual ~SrsOnBWDonePacket(); 1549 | public: 1550 | virtual int get_perfer_cid(); 1551 | public: 1552 | virtual int get_message_type(); 1553 | protected: 1554 | virtual int get_size(); 1555 | virtual int encode_packet(SrsStream* stream); 1556 | }; 1557 | 1558 | /** 1559 | * onStatus command, AMF0 Call 1560 | * @remark, user must set the stream_id by SrsMessage.set_packet(). 1561 | */ 1562 | class SrsOnStatusCallPacket : public SrsPacket 1563 | { 1564 | private: 1565 | typedef SrsPacket super; 1566 | protected: 1567 | virtual const char* get_class_name() 1568 | { 1569 | return CLASS_NAME_STRING(SrsOnStatusCallPacket); 1570 | } 1571 | public: 1572 | std::string command_name; 1573 | double transaction_id; 1574 | SrsAmf0Null* args; 1575 | SrsAmf0Object* data; 1576 | public: 1577 | SrsOnStatusCallPacket(); 1578 | virtual ~SrsOnStatusCallPacket(); 1579 | public: 1580 | virtual int get_perfer_cid(); 1581 | public: 1582 | virtual int get_message_type(); 1583 | protected: 1584 | virtual int get_size(); 1585 | virtual int encode_packet(SrsStream* stream); 1586 | }; 1587 | 1588 | /** 1589 | * onStatus data, AMF0 Data 1590 | * @remark, user must set the stream_id by SrsMessage.set_packet(). 1591 | */ 1592 | class SrsOnStatusDataPacket : public SrsPacket 1593 | { 1594 | private: 1595 | typedef SrsPacket super; 1596 | protected: 1597 | virtual const char* get_class_name() 1598 | { 1599 | return CLASS_NAME_STRING(SrsOnStatusDataPacket); 1600 | } 1601 | public: 1602 | std::string command_name; 1603 | SrsAmf0Object* data; 1604 | public: 1605 | SrsOnStatusDataPacket(); 1606 | virtual ~SrsOnStatusDataPacket(); 1607 | public: 1608 | virtual int get_perfer_cid(); 1609 | public: 1610 | virtual int get_message_type(); 1611 | protected: 1612 | virtual int get_size(); 1613 | virtual int encode_packet(SrsStream* stream); 1614 | }; 1615 | 1616 | /** 1617 | * AMF0Data RtmpSampleAccess 1618 | * @remark, user must set the stream_id by SrsMessage.set_packet(). 1619 | */ 1620 | class SrsSampleAccessPacket : public SrsPacket 1621 | { 1622 | private: 1623 | typedef SrsPacket super; 1624 | protected: 1625 | virtual const char* get_class_name() 1626 | { 1627 | return CLASS_NAME_STRING(SrsSampleAccessPacket); 1628 | } 1629 | public: 1630 | std::string command_name; 1631 | bool video_sample_access; 1632 | bool audio_sample_access; 1633 | public: 1634 | SrsSampleAccessPacket(); 1635 | virtual ~SrsSampleAccessPacket(); 1636 | public: 1637 | virtual int get_perfer_cid(); 1638 | public: 1639 | virtual int get_message_type(); 1640 | protected: 1641 | virtual int get_size(); 1642 | virtual int encode_packet(SrsStream* stream); 1643 | }; 1644 | 1645 | /** 1646 | * the stream metadata. 1647 | * FMLE: @setDataFrame 1648 | * others: onMetaData 1649 | */ 1650 | class SrsOnMetaDataPacket : public SrsPacket 1651 | { 1652 | private: 1653 | typedef SrsPacket super; 1654 | protected: 1655 | virtual const char* get_class_name() 1656 | { 1657 | return CLASS_NAME_STRING(SrsOnMetaDataPacket); 1658 | } 1659 | public: 1660 | std::string name; 1661 | SrsAmf0Object* metadata; 1662 | public: 1663 | SrsOnMetaDataPacket(); 1664 | virtual ~SrsOnMetaDataPacket(); 1665 | public: 1666 | virtual int decode(SrsStream* stream); 1667 | public: 1668 | virtual int get_perfer_cid(); 1669 | public: 1670 | virtual int get_message_type(); 1671 | protected: 1672 | virtual int get_size(); 1673 | virtual int encode_packet(SrsStream* stream); 1674 | }; 1675 | 1676 | /** 1677 | * 5.5. Window Acknowledgement Size (5) 1678 | * The client or the server sends this message to inform the peer which 1679 | * window size to use when sending acknowledgment. 1680 | */ 1681 | class SrsSetWindowAckSizePacket : public SrsPacket 1682 | { 1683 | private: 1684 | typedef SrsPacket super; 1685 | protected: 1686 | virtual const char* get_class_name() 1687 | { 1688 | return CLASS_NAME_STRING(SrsSetWindowAckSizePacket); 1689 | } 1690 | public: 1691 | int32_t ackowledgement_window_size; 1692 | public: 1693 | SrsSetWindowAckSizePacket(); 1694 | virtual ~SrsSetWindowAckSizePacket(); 1695 | public: 1696 | virtual int decode(SrsStream* stream); 1697 | public: 1698 | virtual int get_perfer_cid(); 1699 | public: 1700 | virtual int get_message_type(); 1701 | protected: 1702 | virtual int get_size(); 1703 | virtual int encode_packet(SrsStream* stream); 1704 | }; 1705 | 1706 | /** 1707 | * 5.3. Acknowledgement (3) 1708 | * The client or the server sends the acknowledgment to the peer after 1709 | * receiving bytes equal to the window size. 1710 | */ 1711 | class SrsAcknowledgementPacket : public SrsPacket 1712 | { 1713 | private: 1714 | typedef SrsPacket super; 1715 | protected: 1716 | virtual const char* get_class_name() 1717 | { 1718 | return CLASS_NAME_STRING(SrsAcknowledgementPacket); 1719 | } 1720 | public: 1721 | int32_t sequence_number; 1722 | public: 1723 | SrsAcknowledgementPacket(); 1724 | virtual ~SrsAcknowledgementPacket(); 1725 | public: 1726 | virtual int get_perfer_cid(); 1727 | public: 1728 | virtual int get_message_type(); 1729 | protected: 1730 | virtual int get_size(); 1731 | virtual int encode_packet(SrsStream* stream); 1732 | }; 1733 | 1734 | /** 1735 | * 7.1. Set Chunk Size 1736 | * Protocol control message 1, Set Chunk Size, is used to notify the 1737 | * peer about the new maximum chunk size. 1738 | */ 1739 | class SrsSetChunkSizePacket : public SrsPacket 1740 | { 1741 | private: 1742 | typedef SrsPacket super; 1743 | protected: 1744 | virtual const char* get_class_name() 1745 | { 1746 | return CLASS_NAME_STRING(SrsSetChunkSizePacket); 1747 | } 1748 | public: 1749 | int32_t chunk_size; 1750 | public: 1751 | SrsSetChunkSizePacket(); 1752 | virtual ~SrsSetChunkSizePacket(); 1753 | public: 1754 | virtual int decode(SrsStream* stream); 1755 | public: 1756 | virtual int get_perfer_cid(); 1757 | public: 1758 | virtual int get_message_type(); 1759 | protected: 1760 | virtual int get_size(); 1761 | virtual int encode_packet(SrsStream* stream); 1762 | }; 1763 | 1764 | /** 1765 | * 5.6. Set Peer Bandwidth (6) 1766 | * The client or the server sends this message to update the output 1767 | * bandwidth of the peer. 1768 | */ 1769 | class SrsSetPeerBandwidthPacket : public SrsPacket 1770 | { 1771 | private: 1772 | typedef SrsPacket super; 1773 | protected: 1774 | virtual const char* get_class_name() 1775 | { 1776 | return CLASS_NAME_STRING(SrsSetPeerBandwidthPacket); 1777 | } 1778 | public: 1779 | int32_t bandwidth; 1780 | int8_t type; 1781 | public: 1782 | SrsSetPeerBandwidthPacket(); 1783 | virtual ~SrsSetPeerBandwidthPacket(); 1784 | public: 1785 | virtual int get_perfer_cid(); 1786 | public: 1787 | virtual int get_message_type(); 1788 | protected: 1789 | virtual int get_size(); 1790 | virtual int encode_packet(SrsStream* stream); 1791 | }; 1792 | 1793 | // 3.7. User Control message 1794 | enum SrcPCUCEventType 1795 | { 1796 | // generally, 4bytes event-data 1797 | SrcPCUCStreamBegin = 0x00, 1798 | SrcPCUCStreamEOF = 0x01, 1799 | SrcPCUCStreamDry = 0x02, 1800 | SrcPCUCSetBufferLength = 0x03, // 8bytes event-data 1801 | SrcPCUCStreamIsRecorded = 0x04, 1802 | SrcPCUCPingRequest = 0x06, 1803 | SrcPCUCPingResponse = 0x07, 1804 | }; 1805 | 1806 | /** 1807 | * for the EventData is 4bytes. 1808 | * Stream Begin(=0) 4-bytes stream ID 1809 | * Stream EOF(=1) 4-bytes stream ID 1810 | * StreamDry(=2) 4-bytes stream ID 1811 | * SetBufferLength(=3) 8-bytes 4bytes stream ID, 4bytes buffer length. 1812 | * StreamIsRecorded(=4) 4-bytes stream ID 1813 | * PingRequest(=6) 4-bytes timestamp local server time 1814 | * PingResponse(=7) 4-bytes timestamp received ping request. 1815 | * 1816 | * 3.7. User Control message 1817 | * +------------------------------+------------------------- 1818 | * | Event Type ( 2- bytes ) | Event Data 1819 | * +------------------------------+------------------------- 1820 | * Figure 5 Pay load for the ‘User Control Message’. 1821 | */ 1822 | class SrsUserControlPacket : public SrsPacket 1823 | { 1824 | private: 1825 | typedef SrsPacket super; 1826 | protected: 1827 | virtual const char* get_class_name() 1828 | { 1829 | return CLASS_NAME_STRING(SrsUserControlPacket); 1830 | } 1831 | public: 1832 | // @see: SrcPCUCEventType 1833 | int16_t event_type; 1834 | int32_t event_data; 1835 | /** 1836 | * 4bytes if event_type is SetBufferLength; otherwise 0. 1837 | */ 1838 | int32_t extra_data; 1839 | public: 1840 | SrsUserControlPacket(); 1841 | virtual ~SrsUserControlPacket(); 1842 | public: 1843 | virtual int decode(SrsStream* stream); 1844 | public: 1845 | virtual int get_perfer_cid(); 1846 | public: 1847 | virtual int get_message_type(); 1848 | protected: 1849 | virtual int get_size(); 1850 | virtual int encode_packet(SrsStream* stream); 1851 | }; 1852 | 1853 | /** 1854 | * expect a specified message, drop others util got specified one. 1855 | * @pmsg, user must free it. NULL if not success. 1856 | * @ppacket, store in the pmsg, user must never free it. NULL if not success. 1857 | * @remark, only when success, user can use and must free the pmsg/ppacket. 1858 | */ 1859 | template<class T> 1860 | int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) 1861 | { 1862 | *pmsg = NULL; 1863 | *ppacket = NULL; 1864 | 1865 | int ret = ERROR_SUCCESS; 1866 | 1867 | while (true) { 1868 | SrsCommonMessage* msg = NULL; 1869 | if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { 1870 | srs_error("recv message failed. ret=%d", ret); 1871 | return ret; 1872 | } 1873 | srs_verbose("recv message success."); 1874 | 1875 | if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { 1876 | delete msg; 1877 | srs_error("decode message failed. ret=%d", ret); 1878 | return ret; 1879 | } 1880 | 1881 | T* pkt = dynamic_cast<T*>(msg->get_packet()); 1882 | if (!pkt) { 1883 | delete msg; 1884 | srs_trace("drop message(type=%d, size=%d, time=%d, sid=%d).", 1885 | msg->header.message_type, msg->header.payload_length, 1886 | msg->header.timestamp, msg->header.stream_id); 1887 | continue; 1888 | } 1889 | 1890 | *pmsg = msg; 1891 | *ppacket = pkt; 1892 | break; 1893 | } 1894 | 1895 | return ret; 1896 | } 1897 | 1898 | /** 1899 | * implements the client role protocol. 1900 | */ 1901 | class SrsRtmpClient 1902 | { 1903 | private: 1904 | SrsProtocol* protocol; 1905 | st_netfd_t stfd; 1906 | public: 1907 | SrsRtmpClient(st_netfd_t _stfd); 1908 | virtual ~SrsRtmpClient(); 1909 | public: 1910 | virtual void set_recv_timeout(int64_t timeout_us); 1911 | virtual void set_send_timeout(int64_t timeout_us); 1912 | virtual int64_t get_recv_bytes(); 1913 | virtual int64_t get_send_bytes(); 1914 | virtual int get_recv_kbps(); 1915 | virtual int get_send_kbps(); 1916 | virtual int recv_message(SrsCommonMessage** pmsg); 1917 | virtual int send_message(ISrsMessage* msg); 1918 | public: 1919 | virtual int handshake(); 1920 | virtual int connect_app(std::string app, std::string tc_url); 1921 | virtual int create_stream(int& stream_id); 1922 | virtual int play(std::string stream, int stream_id); 1923 | virtual int publish(std::string stream, int stream_id); 1924 | }; 1925 | 1926 | #endif 1927 | -------------------------------------------------------------------------------- /src/app/htl_app_task_base.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <inttypes.h> 4 | #include <stdlib.h> 5 | 6 | #include <string> 7 | #include <sstream> 8 | using namespace std; 9 | 10 | #include <htl_core_error.hpp> 11 | #include <htl_core_log.hpp> 12 | #include <htl_app_http_client.hpp> 13 | 14 | #include <htl_app_task_base.hpp> 15 | 16 | StBaseTask::StBaseTask(){ 17 | } 18 | 19 | StBaseTask::~StBaseTask(){ 20 | } 21 | 22 | int StBaseTask::InitializeBase(std::string http_url, double startup, double delay, double error, int count){ 23 | int ret = ERROR_SUCCESS; 24 | 25 | if((ret = GetUri()->Initialize(http_url)) != ERROR_SUCCESS){ 26 | return ret; 27 | } 28 | 29 | Info("task url(%s) parsed, startup=%.2f, delay=%.2f, error=%.2f, count=%d", http_url.c_str(), startup, delay, error, count); 30 | 31 | this->delay_seconds = delay; 32 | this->startup_seconds = startup; 33 | this->error_seconds = error; 34 | this->count = count; 35 | 36 | return ret; 37 | } 38 | 39 | int StBaseTask::Process(){ 40 | int ret = ERROR_SUCCESS; 41 | 42 | if(startup_seconds > 0){ 43 | int sleep_ms = StUtility::BuildRandomMTime(startup_seconds); 44 | Trace("start random sleep %dms", sleep_ms); 45 | st_usleep(sleep_ms * 1000); 46 | } 47 | 48 | if((ret = ProcessTask()) != ERROR_SUCCESS){ 49 | return ret; 50 | } 51 | 52 | return ret; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/app/htl_app_task_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_app_task_base_hpp 2 | #define _htl_app_task_base_hpp 3 | 4 | /* 5 | #include <htl_app_task_base.hpp> 6 | */ 7 | #include <string> 8 | 9 | #include <http_parser.h> 10 | #include <htl_core_uri.hpp> 11 | 12 | #include <htl_os_st.hpp> 13 | 14 | class StBaseTask : public StTask 15 | { 16 | protected: 17 | double startup_seconds; 18 | double delay_seconds; 19 | double error_seconds; 20 | int count; 21 | public: 22 | StBaseTask(); 23 | virtual ~StBaseTask(); 24 | protected: 25 | virtual int InitializeBase(std::string http_url, double startup, double delay, double error, int count); 26 | public: 27 | virtual int Process(); 28 | protected: 29 | virtual Uri* GetUri() = 0; 30 | virtual int ProcessTask() = 0; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/core/htl_core_aggregate_ret.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <vector> 4 | using namespace std; 5 | 6 | #include <htl_core_error.hpp> 7 | 8 | #include <htl_core_aggregate_ret.hpp> 9 | 10 | AggregateRet::AggregateRet(){ 11 | } 12 | 13 | AggregateRet::~AggregateRet(){ 14 | } 15 | 16 | void AggregateRet::Add(int ret){ 17 | rets.push_back(ret); 18 | } 19 | 20 | int AggregateRet::GetReturnValue(){ 21 | int ret = ERROR_SUCCESS; 22 | 23 | for(vector<int>::iterator ite = rets.begin(); ite != rets.end(); ++ite){ 24 | int item_ret = *ite; 25 | 26 | if((ret = item_ret) != ERROR_SUCCESS){ 27 | return ret; 28 | } 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/core/htl_core_aggregate_ret.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_core_aggregate_ret_hpp 2 | #define _htl_core_aggregate_ret_hpp 3 | 4 | /* 5 | #include <htl_core_aggregate_ret.hpp> 6 | */ 7 | #include <vector> 8 | 9 | class AggregateRet 10 | { 11 | private: 12 | std::vector<int> rets; 13 | public: 14 | AggregateRet(); 15 | virtual ~AggregateRet(); 16 | public: 17 | /** 18 | * add return value to collection. 19 | */ 20 | virtual void Add(int ret); 21 | /** 22 | * get the summary of return value, if any failed, return it. 23 | */ 24 | virtual int GetReturnValue(); 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/core/htl_core_error.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/core/htl_core_error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_core_error_hpp 2 | #define _htl_core_error_hpp 3 | 4 | /* 5 | #include <htl_core_error.hpp> 6 | */ 7 | 8 | #define ERROR_SUCCESS 0 9 | 10 | #define ERROR_SOCKET 100 11 | #define ERROR_OPEN_SOCKET 101 12 | #define ERROR_CONNECT 102 13 | #define ERROR_SEND 103 14 | #define ERROR_READ 104 15 | #define ERROR_CLOSE 105 16 | #define ERROR_DNS_RESOLVE 106 17 | 18 | #define ERROR_URL_INVALID 200 19 | #define ERROR_HTTP_RESPONSE 201 20 | #define ERROR_HLS_INVALID 202 21 | 22 | #define ERROR_NOT_SUPPORT 300 23 | 24 | #define ERROR_ST_INITIALIZE 400 25 | #define ERROR_ST_THREAD_CREATE 401 26 | 27 | #define ERROR_HP_PARSE_URL 500 28 | #define ERROR_HP_EP_CHNAGED 501 29 | #define ERROR_HP_PARSE_RESPONSE 502 30 | 31 | #define ERROR_RTMP_URL 600 32 | #define ERROR_RTMP_OVERFLOW 601 33 | #define ERROR_RTMP_MSG_TOO_BIG 602 34 | #define ERROR_RTMP_INVALID_RESPONSE 603 35 | 36 | #define ProductVersion "1.0" 37 | #define ProductHTTPName "Bravo HttpLoad/"ProductVersion 38 | #define ProductHLSName "Bravo HlsLoad/"ProductVersion 39 | #define BuildPlatform "linux" 40 | #define BugReportEmail "winterserver@126.com" 41 | 42 | #define HTTP_HEADER_BUFFER 1024 43 | #define HTTP_BODY_BUFFER 32*1024 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /src/core/htl_core_log.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <string.h> 4 | #include <sys/time.h> 5 | 6 | #include <htl_core_log.hpp> 7 | 8 | DateTime::DateTime(){ 9 | memset(time_data, 0, DATE_LEN); 10 | } 11 | 12 | DateTime::~DateTime(){ 13 | } 14 | 15 | const char* DateTime::FormatTime(){ 16 | // clock time 17 | timeval tv; 18 | if(gettimeofday(&tv, NULL) == -1){ 19 | return ""; 20 | } 21 | // to calendar time 22 | struct tm* tm; 23 | if((tm = localtime(&tv.tv_sec)) == NULL){ 24 | return ""; 25 | } 26 | 27 | // log header, the time/pid/level of log 28 | // reserved 1bytes for the new line. 29 | snprintf(time_data, DATE_LEN, "%d-%02d-%02d %02d:%02d:%02d.%03d", 30 | 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, 31 | (int)(tv.tv_usec / 1000)); 32 | 33 | return time_data; 34 | } 35 | 36 | LogContext::LogContext(){ 37 | } 38 | 39 | LogContext::~LogContext(){ 40 | } 41 | 42 | int LogContext::GetId(){ 43 | return 0; 44 | } 45 | 46 | const char* LogContext::FormatTime(){ 47 | return time.FormatTime(); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/core/htl_core_log.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_core_log_hpp 2 | #define _htl_core_log_hpp 3 | 4 | /* 5 | #include <htl_core_log.hpp> 6 | */ 7 | 8 | #include <stdio.h> 9 | 10 | #include <errno.h> 11 | #include <string.h> 12 | 13 | #include <string> 14 | 15 | class DateTime 16 | { 17 | private: 18 | // %d-%02d-%02d %02d:%02d:%02d.%03d 19 | #define DATE_LEN 24 20 | char time_data[DATE_LEN]; 21 | public: 22 | DateTime(); 23 | virtual ~DateTime(); 24 | public: 25 | virtual const char* FormatTime(); 26 | }; 27 | 28 | class LogContext 29 | { 30 | private: 31 | DateTime time; 32 | public: 33 | LogContext(); 34 | virtual ~LogContext(); 35 | public: 36 | virtual void SetId(int id) = 0; 37 | virtual int GetId(); 38 | public: 39 | virtual const char* FormatTime(); 40 | }; 41 | 42 | // user must implements the LogContext and define a global instance. 43 | extern LogContext* context; 44 | 45 | #if 1 46 | #define Verbose(msg, ...) printf("[%s][%d][verbs] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n") 47 | #define Info(msg, ...) printf("[%s][%d][infos] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n") 48 | #define Trace(msg, ...) printf("[%s][%d][trace] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n") 49 | #define Warn(msg, ...) printf("[%s][%d][warns] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") 50 | #define Error(msg, ...) printf("[%s][%d][error] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") 51 | #else 52 | #define Verbose(msg, ...) printf("[%s][%d][verbs][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") 53 | #define Info(msg, ...) printf("[%s][%d][infos][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") 54 | #define Trace(msg, ...) printf("[%s][%d][trace][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") 55 | #define Warn(msg, ...) printf("[%s][%d][warns][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") 56 | #define Error(msg, ...) printf("[%s][%d][error][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") 57 | #endif 58 | 59 | #if 1 60 | #undef Verbose 61 | #define Verbose(msg, ...) (void)0 62 | #endif 63 | #if 1 64 | #undef Info 65 | #define Info(msg, ...) (void)0 66 | #endif 67 | 68 | // for summary/report thread, print to stderr. 69 | #define LReport(msg, ...) fprintf(stderr, "[%s] ", context->FormatTime());fprintf(stderr, msg, ##__VA_ARGS__);fprintf(stderr, "\n") 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/core/htl_core_uri.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <stdlib.h> 4 | 5 | #include <string> 6 | #include <sstream> 7 | using namespace std; 8 | 9 | #include <htl_core_log.hpp> 10 | #include <htl_core_error.hpp> 11 | 12 | #include <htl_core_uri.hpp> 13 | 14 | Uri::Uri(){ 15 | } 16 | 17 | Uri::~Uri(){ 18 | } 19 | 20 | ProtocolUrl::ProtocolUrl(){ 21 | } 22 | 23 | ProtocolUrl::~ProtocolUrl(){ 24 | } 25 | 26 | int ProtocolUrl::Initialize(std::string http_url){ 27 | int ret = ERROR_SUCCESS; 28 | 29 | url = http_url; 30 | const char* purl = url.c_str(); 31 | 32 | if((ret = http_parser_parse_url(purl, url.length(), 0, &hp_u)) != 0){ 33 | int code = ret; 34 | ret = ERROR_HP_PARSE_URL; 35 | 36 | Error("parse url %s failed, code=%d, ret=%d", purl, code, ret); 37 | return ret; 38 | } 39 | 40 | if(Get(UF_SCHEMA) != ""){ 41 | schema = Get(UF_SCHEMA); 42 | } 43 | 44 | host = Get(UF_HOST); 45 | 46 | if(Get(UF_PORT) != ""){ 47 | port = atoi(Get(UF_PORT).c_str()); 48 | } 49 | 50 | path = Get(UF_PATH); 51 | 52 | return ret; 53 | } 54 | 55 | const char* ProtocolUrl::GetUrl(){ 56 | return url.c_str(); 57 | } 58 | 59 | const char* ProtocolUrl::GetSchema(){ 60 | return schema.c_str(); 61 | } 62 | 63 | const char* ProtocolUrl::GetHost(){ 64 | return host.c_str(); 65 | } 66 | 67 | int ProtocolUrl::GetPort(){ 68 | return port; 69 | } 70 | 71 | string ProtocolUrl::Get(http_parser_url_fields field){ 72 | return HttpUrl::GetUriField(url, &hp_u, field); 73 | } 74 | 75 | string ProtocolUrl::GetUriField(string uri, http_parser_url* hp_u, http_parser_url_fields field){ 76 | if((hp_u->field_set & (1 << field)) == 0){ 77 | return ""; 78 | } 79 | 80 | Verbose("uri field matched, off=%d, len=%d, value=%.*s", 81 | hp_u->field_data[field].off, hp_u->field_data[field].len, hp_u->field_data[field].len, 82 | uri.c_str() + hp_u->field_data[field].off); 83 | 84 | return uri.substr(hp_u->field_data[field].off, hp_u->field_data[field].len); 85 | } 86 | 87 | HttpUrl::HttpUrl(){ 88 | port = 80; 89 | } 90 | 91 | HttpUrl::~HttpUrl(){ 92 | } 93 | 94 | #define PROTOCOL_HTTP "http://" 95 | #define PROTOCOL_HTTPS "https://" 96 | string HttpUrl::Resolve(string origin_url){ 97 | string copy = origin_url; 98 | 99 | size_t pos = string::npos; 100 | string key = "./"; 101 | if((pos = origin_url.find(key)) == 0){ 102 | copy = origin_url.substr(key.length()); 103 | } 104 | 105 | // uri 106 | if(copy.find(PROTOCOL_HTTP) == 0 || copy.find(PROTOCOL_HTTPS) == 0){ 107 | return copy; 108 | } 109 | 110 | // abs or relative url 111 | stringstream ss; 112 | ss << schema << "://" << host; 113 | 114 | if(port != 80){ 115 | ss << ":" << port; 116 | } 117 | 118 | // relative path 119 | if(copy.find("/") != 0){ 120 | string dir = path; 121 | 122 | if((pos = dir.rfind("/")) != string::npos){ 123 | dir = dir.substr(0, pos); 124 | } 125 | 126 | ss << dir << "/"; 127 | } 128 | 129 | ss << copy; 130 | 131 | return ss.str(); 132 | } 133 | 134 | HttpUrl* HttpUrl::Copy(){ 135 | HttpUrl* copy = new HttpUrl(); 136 | 137 | copy->Initialize(url); 138 | 139 | return copy; 140 | } 141 | 142 | const char* HttpUrl::GetPath(){ 143 | return path.c_str(); 144 | } 145 | 146 | RtmpUrl::RtmpUrl(){ 147 | port = 1935; 148 | } 149 | 150 | RtmpUrl::~RtmpUrl(){ 151 | } 152 | 153 | int RtmpUrl::Initialize(std::string http_url){ 154 | int ret = ERROR_SUCCESS; 155 | 156 | if((ret = ProtocolUrl::Initialize(http_url)) != ERROR_SUCCESS){ 157 | return ret; 158 | } 159 | 160 | // TODO: support rewrite vhost in query. 161 | vhost = host; 162 | 163 | app = path.c_str() + 1; 164 | 165 | size_t pos = string::npos; 166 | if((pos = app.find("/")) == string::npos){ 167 | ret = ERROR_RTMP_URL; 168 | Error("invalid rtmp url, no stream found, ret=%d", ret); 169 | return ret; 170 | } 171 | 172 | stream = app.substr(pos + 1); 173 | app = app.substr(0, pos); 174 | 175 | stringstream ss; 176 | ss << schema << "://" << vhost << ":" << port << "/" << app; 177 | tcUrl = ss.str(); 178 | 179 | return ret; 180 | } 181 | 182 | const char* RtmpUrl::GetTcUrl(){ 183 | return tcUrl.c_str(); 184 | } 185 | 186 | const char* RtmpUrl::GetVhost(){ 187 | return vhost.c_str(); 188 | } 189 | 190 | const char* RtmpUrl::GetApp(){ 191 | return app.c_str(); 192 | } 193 | 194 | const char* RtmpUrl::GetStream(){ 195 | return stream.c_str(); 196 | } 197 | 198 | -------------------------------------------------------------------------------- /src/core/htl_core_uri.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_core_uri_hpp 2 | #define _htl_core_uri_hpp 3 | 4 | /* 5 | #include <htl_core_uri.hpp> 6 | */ 7 | 8 | #include <string> 9 | 10 | #include <http_parser.h> 11 | 12 | class Uri 13 | { 14 | public: 15 | Uri(); 16 | virtual ~Uri(); 17 | public: 18 | virtual int Initialize(std::string http_url) = 0; 19 | }; 20 | 21 | class ProtocolUrl : public Uri 22 | { 23 | protected: 24 | std::string url; 25 | http_parser_url hp_u; 26 | protected: 27 | std::string schema; 28 | std::string host; 29 | int port; 30 | std::string path; 31 | public: 32 | ProtocolUrl(); 33 | virtual ~ProtocolUrl(); 34 | public: 35 | virtual int Initialize(std::string http_url); 36 | public: 37 | virtual const char* GetUrl(); 38 | virtual const char* GetSchema(); 39 | virtual const char* GetHost(); 40 | virtual int GetPort(); 41 | protected: 42 | virtual std::string Get(http_parser_url_fields field); 43 | /** 44 | * get the parsed url field. 45 | * @return return empty string if not set. 46 | */ 47 | static std::string GetUriField(std::string uri, http_parser_url* hp_u, http_parser_url_fields field); 48 | }; 49 | 50 | class HttpUrl : public ProtocolUrl 51 | { 52 | public: 53 | HttpUrl(); 54 | virtual ~HttpUrl(); 55 | public: 56 | virtual std::string Resolve(std::string origin_url); 57 | virtual HttpUrl* Copy(); 58 | public: 59 | virtual const char* GetPath(); 60 | }; 61 | 62 | class RtmpUrl : public ProtocolUrl 63 | { 64 | private: 65 | std::string tcUrl; 66 | std::string vhost; 67 | std::string app; 68 | std::string stream; 69 | public: 70 | RtmpUrl(); 71 | virtual ~RtmpUrl(); 72 | public: 73 | virtual int Initialize(std::string http_url); 74 | public: 75 | virtual const char* GetTcUrl(); 76 | virtual const char* GetVhost(); 77 | virtual const char* GetApp(); 78 | virtual const char* GetStream(); 79 | }; 80 | 81 | #endif 82 | 83 | -------------------------------------------------------------------------------- /src/core/htl_stdinc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_stdinc_hpp 2 | #define _htl_stdinc_hpp 3 | 4 | /* 5 | #include <htl_stdinc.hpp> 6 | */ 7 | 8 | // for 32bit os, 2G big file limit for unistd io, 9 | // ie. read/write/lseek to use 64bits size for huge file. 10 | #ifndef _FILE_OFFSET_BITS 11 | #define _FILE_OFFSET_BITS 64 12 | #endif 13 | 14 | // for int64_t print using PRId64 format. 15 | #ifndef __STDC_FORMAT_MACROS 16 | #define __STDC_FORMAT_MACROS 17 | #endif 18 | 19 | #endif 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/htl_main_hls_load.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <getopt.h> 4 | #include <stdlib.h> 5 | 6 | #include <string> 7 | using namespace std; 8 | 9 | // project lib 10 | #include <htl_core_log.hpp> 11 | #include <htl_core_error.hpp> 12 | #include <htl_app_hls_load.hpp> 13 | 14 | #include <htl_main_utility.hpp> 15 | 16 | #define DefaultDelaySeconds -1 17 | #define DefaultHttpUrl "http://127.0.0.1:3080/hls/hls.m3u8" 18 | #define DefaultVod false 19 | 20 | int discovery_options(int argc, char** argv, 21 | bool& show_help, bool& show_version, string& url, bool& vod, int& threads, 22 | double& startup, double& delay, double& error, double& report, int& count) 23 | { 24 | int ret = ERROR_SUCCESS; 25 | 26 | static option long_options[] = { 27 | SharedOptions() 28 | {"vod", no_argument, 0, 'o'}, 29 | {0, 0, 0, 0} 30 | }; 31 | 32 | int opt = 0; 33 | int option_index = 0; 34 | while((opt = getopt_long(argc, argv, "hvc:r:t:os:d:e:m:", long_options, &option_index)) != -1){ 35 | switch(opt){ 36 | case 'o': 37 | vod = true; 38 | break; 39 | ProcessSharedOptions() 40 | default: 41 | show_help = true; 42 | break; 43 | } 44 | } 45 | 46 | // check values 47 | if(url == ""){ 48 | show_help = true; 49 | return ret; 50 | } 51 | 52 | return ret; 53 | } 54 | 55 | void help(char** argv){ 56 | printf("%s, Copyright (c) 2013 winlin\n", ProductHTTPName); 57 | 58 | printf("" 59 | "Usage: %s <Options> <-u URL>\n" 60 | "%s base on st(state-threads), support huge concurrency.\n" 61 | "Options:\n" 62 | ShowHelpPart1() 63 | " -o, --vod Whether url is vod, loop the m3u8 file list. default is %s\n" 64 | ShowHelpPart2() 65 | "\n" 66 | "\n" 67 | "Examples:\n" 68 | "1. start a client\n" 69 | " %s -c 1 -r %s\n" 70 | "2. start 1000 clients\n" 71 | " %s -c 1000 -r %s\n" 72 | "3. start 10000 clients\n" 73 | " %s -c 10000 -r %s\n" 74 | "4. start 100000 clients\n" 75 | " %s -c 100000 -r %s\n" 76 | "5. start 10000 vod clients\n" 77 | " %s -c 10000 -o -r %s\n" 78 | "\n" 79 | "This program built for %s.\n" 80 | "Report bugs to <%s>\n", 81 | argv[0], argv[0], 82 | DefaultThread, DefaultHttpUrl, DefaultCount, // part1 83 | (DefaultVod? "true":"false"), // vod 84 | (double)DefaultStartupSeconds, (double)DefaultDelaySeconds, // part2 85 | DefaultErrorSeconds, DefaultReportSeconds, // part2 86 | argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, 87 | argv[0], DefaultHttpUrl, 88 | BuildPlatform, BugReportEmail); 89 | 90 | exit(0); 91 | } 92 | 93 | int main(int argc, char** argv){ 94 | int ret = ERROR_SUCCESS; 95 | 96 | bool show_help = false, show_version = false; 97 | string url; bool vod = DefaultVod; int threads = DefaultThread; 98 | double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds; 99 | double report = DefaultReportSeconds; int count = DefaultCount; 100 | 101 | if((ret = discovery_options(argc, argv, show_help, show_version, url, vod, threads, start, delay, error, report, count)) != ERROR_SUCCESS){ 102 | Error("discovery options failed. ret=%d", ret); 103 | return ret; 104 | } 105 | Trace("params url=%s, vod=%d, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d", 106 | url.c_str(), vod, threads, start, delay, error, report, count); 107 | 108 | if(show_help){ 109 | help(argv); 110 | } 111 | if(show_version){ 112 | version(); 113 | } 114 | 115 | StFarm farm; 116 | 117 | if((ret = farm.Initialize(report)) != ERROR_SUCCESS){ 118 | Error("initialize the farm failed. ret=%d", ret); 119 | return ret; 120 | } 121 | 122 | for(int i = 0; i < threads; i++){ 123 | StHlsTask* task = new StHlsTask(); 124 | 125 | if((ret = task->Initialize(url, vod, start, delay, error, count)) != ERROR_SUCCESS){ 126 | Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret); 127 | return ret; 128 | } 129 | 130 | if((ret = farm.Spawn(task)) != ERROR_SUCCESS){ 131 | Error("st farm spwan task failed, ret=%d", ret); 132 | return ret; 133 | } 134 | } 135 | 136 | farm.WaitAll(); 137 | 138 | return 0; 139 | } 140 | 141 | -------------------------------------------------------------------------------- /src/main/htl_main_http_load.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <getopt.h> 4 | #include <stdlib.h> 5 | 6 | #include <string> 7 | using namespace std; 8 | 9 | // project lib 10 | #include <htl_core_log.hpp> 11 | #include <htl_core_error.hpp> 12 | #include <htl_app_http_load.hpp> 13 | 14 | #include <htl_main_utility.hpp> 15 | 16 | #define DefaultDelaySeconds 0.0 17 | #define DefaultHttpUrl "http://127.0.0.1:3080/hls/segm130813144315787-522881.ts" 18 | 19 | int discovery_options(int argc, char** argv, 20 | bool& show_help, bool& show_version, string& url, int& threads, 21 | double& startup, double& delay, double& error, double& report, int& count 22 | ){ 23 | int ret = ERROR_SUCCESS; 24 | 25 | static option long_options[] = { 26 | SharedOptions() 27 | {0, 0, 0, 0} 28 | }; 29 | 30 | int opt = 0; 31 | int option_index = 0; 32 | while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:", long_options, &option_index)) != -1){ 33 | switch(opt){ 34 | ProcessSharedOptions() 35 | default: 36 | show_help = true; 37 | break; 38 | } 39 | } 40 | 41 | // check values 42 | if(url == ""){ 43 | show_help = true; 44 | return ret; 45 | } 46 | 47 | return ret; 48 | } 49 | 50 | void help(char** argv){ 51 | printf("%s, Copyright (c) 2013 winlin\n", ProductHTTPName); 52 | 53 | printf("" 54 | "Usage: %s <Options> <-u URL>\n" 55 | "%s base on st(state-threads), support huge concurrency.\n" 56 | "Options:\n" 57 | ShowHelpPart1() 58 | ShowHelpPart2() 59 | "\n" 60 | "Examples:\n" 61 | "1. start a client\n" 62 | " %s -c 1 -r %s\n" 63 | "2. start 1000 clients\n" 64 | " %s -c 1000 -r %s\n" 65 | "3. start 10000 clients\n" 66 | " %s -c 10000 -r %s\n" 67 | "4. start 100000 clients\n" 68 | " %s -c 100000 -r %s\n" 69 | "\n" 70 | "This program built for %s.\n" 71 | "Report bugs to <%s>\n", 72 | argv[0], argv[0], 73 | DefaultThread, DefaultHttpUrl, DefaultCount, // part1 74 | (double)DefaultStartupSeconds, DefaultDelaySeconds, // part2 75 | DefaultErrorSeconds, DefaultReportSeconds, // part2 76 | argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, 77 | BuildPlatform, BugReportEmail); 78 | 79 | exit(0); 80 | } 81 | 82 | int main(int argc, char** argv){ 83 | int ret = ERROR_SUCCESS; 84 | 85 | bool show_help = false, show_version = false; 86 | string url; int threads = DefaultThread; 87 | double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds; 88 | double report = DefaultReportSeconds; int count = DefaultCount; 89 | 90 | if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count)) != ERROR_SUCCESS){ 91 | Error("discovery options failed. ret=%d", ret); 92 | return ret; 93 | } 94 | Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d", 95 | url.c_str(), threads, start, delay, error, report, count); 96 | 97 | if(show_help){ 98 | help(argv); 99 | } 100 | if(show_version){ 101 | version(); 102 | } 103 | 104 | StFarm farm; 105 | 106 | if((ret = farm.Initialize(report)) != ERROR_SUCCESS){ 107 | Error("initialize the farm failed. ret=%d", ret); 108 | return ret; 109 | } 110 | 111 | for(int i = 0; i < threads; i++){ 112 | StHttpTask* task = new StHttpTask(); 113 | 114 | if((ret = task->Initialize(url, start, delay, error, count)) != ERROR_SUCCESS){ 115 | Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret); 116 | return ret; 117 | } 118 | 119 | if((ret = farm.Spawn(task)) != ERROR_SUCCESS){ 120 | Error("st farm spwan task failed, ret=%d", ret); 121 | return ret; 122 | } 123 | } 124 | 125 | farm.WaitAll(); 126 | 127 | return 0; 128 | } 129 | 130 | -------------------------------------------------------------------------------- /src/main/htl_main_rtmp_load.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | #include <getopt.h> 4 | #include <stdlib.h> 5 | 6 | #include <string> 7 | using namespace std; 8 | 9 | // project lib 10 | #include <htl_core_log.hpp> 11 | #include <htl_core_error.hpp> 12 | #include <htl_app_rtmp_load.hpp> 13 | 14 | #include <htl_main_utility.hpp> 15 | 16 | #define DefaultDelaySeconds 1.0 17 | #define DefaultRtmpUrl "rtmp://127.0.0.1:1935/live/livestream" 18 | 19 | int discovery_options(int argc, char** argv, 20 | bool& show_help, bool& show_version, string& url, int& threads, 21 | double& startup, double& delay, double& error, double& report, int& count 22 | ){ 23 | int ret = ERROR_SUCCESS; 24 | 25 | static option long_options[] = { 26 | SharedOptions() 27 | {0, 0, 0, 0} 28 | }; 29 | 30 | int opt = 0; 31 | int option_index = 0; 32 | while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:", long_options, &option_index)) != -1){ 33 | switch(opt){ 34 | ProcessSharedOptions() 35 | default: 36 | show_help = true; 37 | break; 38 | } 39 | } 40 | 41 | // check values 42 | if(url == ""){ 43 | show_help = true; 44 | return ret; 45 | } 46 | 47 | return ret; 48 | } 49 | 50 | void help(char** argv){ 51 | printf("%s, Copyright (c) 2013 winlin\n", ProductHTTPName); 52 | 53 | printf("" 54 | "Usage: %s <Options> <-u URL>\n" 55 | "%s base on st(state-threads), support huge concurrency.\n" 56 | "Options:\n" 57 | ShowHelpPart1() 58 | ShowHelpPart2() 59 | "\n" 60 | "Examples:\n" 61 | "1. start a client\n" 62 | " %s -c 1 -r %s\n" 63 | "2. start 1000 clients\n" 64 | " %s -c 1000 -r %s\n" 65 | "3. start 10000 clients\n" 66 | " %s -c 10000 -r %s\n" 67 | "4. start 100000 clients\n" 68 | " %s -c 100000 -r %s\n" 69 | "\n" 70 | "This program built for %s.\n" 71 | "Report bugs to <%s>\n", 72 | argv[0], argv[0], 73 | DefaultThread, DefaultRtmpUrl, DefaultCount, // part1 74 | (double)DefaultStartupSeconds, DefaultDelaySeconds, // part2 75 | DefaultErrorSeconds, DefaultReportSeconds, // part2 76 | argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, 77 | BuildPlatform, BugReportEmail); 78 | 79 | exit(0); 80 | } 81 | 82 | int main(int argc, char** argv){ 83 | int ret = ERROR_SUCCESS; 84 | 85 | bool show_help = false, show_version = false; 86 | string url; int threads = DefaultThread; 87 | double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds; 88 | double report = DefaultReportSeconds; int count = DefaultCount; 89 | 90 | if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count)) != ERROR_SUCCESS){ 91 | Error("discovery options failed. ret=%d", ret); 92 | return ret; 93 | } 94 | Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d", 95 | url.c_str(), threads, start, delay, error, report, count); 96 | 97 | if(show_help){ 98 | help(argv); 99 | } 100 | if(show_version){ 101 | version(); 102 | } 103 | 104 | StFarm farm; 105 | 106 | if((ret = farm.Initialize(report)) != ERROR_SUCCESS){ 107 | Error("initialize the farm failed. ret=%d", ret); 108 | return ret; 109 | } 110 | 111 | for(int i = 0; i < threads; i++){ 112 | StRtmpTask* task = new StRtmpTask(); 113 | 114 | if((ret = task->Initialize(url, start, delay, error, count)) != ERROR_SUCCESS){ 115 | Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret); 116 | return ret; 117 | } 118 | 119 | if((ret = farm.Spawn(task)) != ERROR_SUCCESS){ 120 | Error("st farm spwan task failed, ret=%d", ret); 121 | return ret; 122 | } 123 | } 124 | 125 | farm.WaitAll(); 126 | 127 | return 0; 128 | } 129 | 130 | -------------------------------------------------------------------------------- /src/main/htl_main_utility.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/htl_main_utility.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_main_utility_hpp 2 | #define _htl_main_utility_hpp 3 | 4 | /* 5 | #include <htl_main_utility.hpp> 6 | */ 7 | 8 | // global instance for graceful log. 9 | LogContext* context = new StLogContext(); 10 | 11 | #define DefaultThread 1 12 | #define DefaultStartupSeconds 5.0 13 | #define DefaultErrorSeconds 10.0 14 | #define DefaultReportSeconds 30.0 15 | #define DefaultCount 0 16 | 17 | #define SharedOptions()\ 18 | {"clients", required_argument, 0, 'c'}, \ 19 | {"url", required_argument, 0, 'r'}, \ 20 | {"repeat", required_argument, 0, 't'}, \ 21 | \ 22 | {"startup", required_argument, 0, 's'}, \ 23 | {"delay", required_argument, 0, 'd'}, \ 24 | {"error", required_argument, 0, 'e'}, \ 25 | {"summary", required_argument, 0, 'm'}, \ 26 | \ 27 | {"help", no_argument, 0, 'h'}, \ 28 | {"version", no_argument, 0, 'v'}, 29 | 30 | #define ProcessSharedOptions()\ 31 | case 'h': \ 32 | show_help = true; \ 33 | break; \ 34 | case 'v': \ 35 | show_version = true; \ 36 | break; \ 37 | case 'c': \ 38 | threads = atoi(optarg); \ 39 | break; \ 40 | case 'r': \ 41 | url = optarg; \ 42 | break; \ 43 | case 't': \ 44 | count = atoi(optarg); \ 45 | break; \ 46 | case 's': \ 47 | startup = atof(optarg); \ 48 | break; \ 49 | case 'd': \ 50 | delay = atof(optarg); \ 51 | break; \ 52 | case 'e': \ 53 | error = atof(optarg); \ 54 | break; \ 55 | case 'm': \ 56 | report = atof(optarg); \ 57 | break; 58 | 59 | #define ShowHelpPart1()\ 60 | " -c CLIENTS, --clients CLIENTS The concurrency client to start to request HTTP data. defaut: %d\n" \ 61 | " -r URL, --url URL The load test http url for each client to download/process. \n" \ 62 | " -t REPEAT, --repeat REPEAT The repeat is the number for each client to download the url. \n" \ 63 | " ie. %s\n" \ 64 | " default: %d. 0 means infinity.\n" 65 | #define ShowHelpPart2()\ 66 | " -s STARTUP, --start STARTUP The start is the ramdom sleep when thread startup in seconds. \n" \ 67 | " defaut: %.2f. 0 means no delay.\n" \ 68 | " -d DELAY, --delay DELAY The delay is the ramdom sleep when success in seconds. \n" \ 69 | " default: %.2f. 0 means no delay. -1 means to use HLS EXTINF duration(HLS only).\n" \ 70 | " -e ERROR, --error ERROR The error is the sleep when error in seconds. \n" \ 71 | " defaut: %.2f. 0 means no delay. \n" \ 72 | " -m SUMMARY, --summary SUMMARY The summary is the sleep when report in seconds. \n" \ 73 | " etasks is error_tasks, statks is sub_tasks, estatks is error_sub_tasks.\n" \ 74 | " duration is the running time in seconds, tduration is the avarage duation of tasks.\n" \ 75 | " nread/nwrite in Mbps, duration/tduration in seconds.\n" \ 76 | " defaut: %.2f. 0 means no delay. \n" \ 77 | " -v, --version Print the version and exit.\n" \ 78 | " -h, --help Print this help message and exit.\n" 79 | 80 | void version(){ 81 | printf(ProductVersion); 82 | exit(0); 83 | } 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /src/os/htl_os_st.cpp: -------------------------------------------------------------------------------- 1 | #include <htl_stdinc.hpp> 2 | 3 | // system 4 | #include <unistd.h> 5 | #include <errno.h> 6 | #include <stdlib.h> 7 | #include <sys/time.h> 8 | #include <inttypes.h> 9 | 10 | // socket 11 | #include <sys/socket.h> 12 | #include <netinet/in.h> 13 | #include <arpa/inet.h> 14 | 15 | // dns 16 | #include <netdb.h> 17 | 18 | #include <string> 19 | using namespace std; 20 | 21 | #include <htl_core_error.hpp> 22 | #include <htl_core_log.hpp> 23 | 24 | #include <htl_os_st.hpp> 25 | 26 | StStatistic::StStatistic(){ 27 | starttime = StUtility::GetCurrentTime(); 28 | task_duration = 0; 29 | threads = alive = 0; 30 | nread = nwrite = 0; 31 | tasks = err_tasks = sub_tasks = err_sub_tasks = 0; 32 | } 33 | 34 | StStatistic::~StStatistic(){ 35 | } 36 | 37 | void StStatistic::OnRead(int /*tid*/, ssize_t nread_bytes){ 38 | this->nread += nread_bytes; 39 | } 40 | 41 | void StStatistic::OnWrite(int /*tid*/, ssize_t nwrite_bytes){ 42 | this->nwrite += nwrite_bytes; 43 | } 44 | 45 | void StStatistic::OnThreadRun(int /*tid*/){ 46 | threads++; 47 | } 48 | 49 | void StStatistic::OnThreadQuit(int /*tid*/){ 50 | threads--; 51 | } 52 | 53 | void StStatistic::OnTaskStart(int /*tid*/, std::string /*task_url*/){ 54 | alive++; 55 | tasks++; 56 | } 57 | 58 | void StStatistic::OnTaskError(int /*tid*/, int duration_seconds){ 59 | alive--; 60 | err_tasks++; 61 | this->task_duration += duration_seconds * 1000; 62 | } 63 | 64 | void StStatistic::OnTaskEnd(int /*tid*/, int duration_seconds){ 65 | alive--; 66 | this->task_duration += duration_seconds * 1000; 67 | } 68 | 69 | void StStatistic::OnSubTaskStart(int /*tid*/, std::string /*sub_task_url*/){ 70 | sub_tasks++; 71 | } 72 | 73 | void StStatistic::OnSubTaskError(int /*tid*/, int duration_seconds){ 74 | err_sub_tasks++; 75 | this->task_duration += duration_seconds * 1000; 76 | } 77 | 78 | void StStatistic::OnSubTaskEnd(int /*tid*/, int duration_seconds){ 79 | this->task_duration += duration_seconds * 1000; 80 | } 81 | 82 | void StStatistic::DoReport(double sleep_ms){ 83 | for(;;){ 84 | int64_t duration = StUtility::GetCurrentTime() - starttime; 85 | double read_mbps = 0, write_mbps = 0; 86 | 87 | if(duration > 0){ 88 | read_mbps = nread * 8.0 / duration / 1000; 89 | write_mbps = nwrite * 8.0 / duration / 1000; 90 | } 91 | 92 | double avarage_duration = task_duration/1000.0; 93 | if(tasks > 0){ 94 | avarage_duration /= tasks; 95 | } 96 | LReport("[report] [%d] threads:%d alive:%d duration:%.0f tduration:%.0f nread:%.2f nwrite:%.2f " 97 | "tasks:%"PRId64" etasks:%"PRId64" stasks:%"PRId64" estasks:%"PRId64, 98 | getpid(), threads, alive, duration/1000.0, avarage_duration, read_mbps, write_mbps, 99 | tasks, err_tasks, sub_tasks, err_sub_tasks); 100 | 101 | st_usleep((st_utime_t)(sleep_ms * 1000)); 102 | } 103 | } 104 | 105 | StStatistic* statistic = new StStatistic(); 106 | 107 | StTask::StTask(){ 108 | static int _id = 0; 109 | id = ++_id; 110 | } 111 | 112 | StTask::~StTask(){ 113 | } 114 | 115 | int StTask::GetId(){ 116 | return id; 117 | } 118 | 119 | StFarm::StFarm(){ 120 | } 121 | 122 | StFarm::~StFarm(){ 123 | } 124 | 125 | int StFarm::Initialize(double report){ 126 | int ret = ERROR_SUCCESS; 127 | 128 | report_seconds = report; 129 | 130 | // use linux epoll. 131 | if(st_set_eventsys(ST_EVENTSYS_ALT) == -1){ 132 | ret = ERROR_ST_INITIALIZE; 133 | Error("st_set_eventsys use linux epoll failed. ret=%d", ret); 134 | return ret; 135 | } 136 | 137 | if(st_init() != 0){ 138 | ret = ERROR_ST_INITIALIZE; 139 | Error("st_init failed. ret=%d", ret); 140 | return ret; 141 | } 142 | 143 | StUtility::InitRandom(); 144 | 145 | return ret; 146 | } 147 | 148 | int StFarm::Spawn(StTask* task){ 149 | int ret = ERROR_SUCCESS; 150 | 151 | if(st_thread_create(st_thread_function, task, 0, 0) == NULL){ 152 | ret = ERROR_ST_THREAD_CREATE; 153 | Error("crate st_thread failed, ret=%d", ret); 154 | return ret; 155 | } 156 | 157 | Trace("create thread for task #%d success", task->GetId()); 158 | 159 | return ret; 160 | } 161 | 162 | int StFarm::WaitAll(){ 163 | int ret = ERROR_SUCCESS; 164 | 165 | // main thread turn to a report therad. 166 | statistic->DoReport(report_seconds * 1000); 167 | 168 | st_thread_exit(NULL); 169 | 170 | return ret; 171 | } 172 | 173 | void* StFarm::st_thread_function(void* args){ 174 | StTask* task = (StTask*)args; 175 | 176 | context->SetId(task->GetId()); 177 | 178 | statistic->OnThreadRun(task->GetId()); 179 | 180 | int ret = task->Process(); 181 | 182 | statistic->OnThreadQuit(task->GetId()); 183 | 184 | if(ret != ERROR_SUCCESS){ 185 | Warn("st task terminate with ret=%d", ret); 186 | } 187 | else{ 188 | Trace("st task terminate with ret=%d", ret); 189 | } 190 | 191 | delete task; 192 | 193 | return NULL; 194 | } 195 | 196 | StSocket::StSocket(){ 197 | sock_nfd = NULL; 198 | status = SocketInit; 199 | } 200 | 201 | StSocket::~StSocket(){ 202 | Close(); 203 | } 204 | 205 | st_netfd_t StSocket::GetStfd(){ 206 | return sock_nfd; 207 | } 208 | 209 | SocketStatus StSocket::Status(){ 210 | return status; 211 | } 212 | 213 | int StSocket::Connect(const char* ip, int port){ 214 | int ret = ERROR_SUCCESS; 215 | 216 | Close(); 217 | 218 | int sock = socket(AF_INET, SOCK_STREAM, 0); 219 | if(sock == -1){ 220 | ret = ERROR_SOCKET; 221 | Error("create socket error. ret=%d", ret); 222 | return ret; 223 | } 224 | 225 | int reuse_socket = 1; 226 | if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1){ 227 | ret = ERROR_SOCKET; 228 | Error("setsockopt reuse-addr error. ret=%d", ret); 229 | return ret; 230 | } 231 | 232 | int keepalive_socket = 1; 233 | if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive_socket, sizeof(int)) == -1){ 234 | ret = ERROR_SOCKET; 235 | Error("setsockopt keep-alive error. ret=%d", ret); 236 | return ret; 237 | } 238 | 239 | sock_nfd = st_netfd_open_socket(sock); 240 | if(sock_nfd == NULL){ 241 | ret = ERROR_OPEN_SOCKET; 242 | Error("st_netfd_open_socket failed. ret=%d", ret); 243 | return ret; 244 | } 245 | Info("create socket(%d) success", sock); 246 | 247 | 248 | // connect to server 249 | sockaddr_in addr; 250 | addr.sin_family = AF_INET; 251 | addr.sin_port = htons(port); 252 | addr.sin_addr.s_addr = inet_addr(ip); 253 | 254 | if(st_connect(sock_nfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){ 255 | ret = ERROR_CONNECT; 256 | Error("connect to server(%s:%d) error. ret=%d", ip, port, ret); 257 | return ret; 258 | } 259 | Info("connec to server %s at port %d success", ip, port); 260 | 261 | status = SocketConnected; 262 | 263 | return ret; 264 | } 265 | 266 | int StSocket::Read(const void* buf, size_t size, ssize_t* nread){ 267 | int ret = ERROR_SUCCESS; 268 | 269 | *nread = st_read(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT); 270 | 271 | // On success a non-negative integer indicating the number of bytes actually read is returned 272 | // (a value of 0 means the network connection is closed or end of file is reached). 273 | if(*nread <= 0){ 274 | if(*nread == 0){ 275 | errno = ECONNRESET; 276 | } 277 | 278 | ret = ERROR_READ; 279 | status = SocketDisconnected; 280 | } 281 | 282 | if(*nread > 0){ 283 | statistic->OnRead(context->GetId(), *nread); 284 | } 285 | 286 | return ret; 287 | } 288 | 289 | int StSocket::ReadFully(const void* buf, size_t size, ssize_t* nread){ 290 | int ret = ERROR_SUCCESS; 291 | 292 | *nread = st_read_fully(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT); 293 | 294 | // On success a non-negative integer indicating the number of bytes actually read is returned 295 | // (a value less than nbyte means the network connection is closed or end of file is reached) 296 | if(*nread != (ssize_t)size){ 297 | if(*nread >= 0){ 298 | errno = ECONNRESET; 299 | } 300 | 301 | ret = ERROR_READ; 302 | status = SocketDisconnected; 303 | } 304 | 305 | if(*nread > 0){ 306 | statistic->OnRead(context->GetId(), *nread); 307 | } 308 | 309 | return ret; 310 | } 311 | 312 | int StSocket::Write(const void* buf, size_t size, ssize_t* nwrite){ 313 | int ret = ERROR_SUCCESS; 314 | 315 | *nwrite = st_write(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT); 316 | 317 | if(*nwrite <= 0){ 318 | ret = ERROR_SEND; 319 | status = SocketDisconnected; 320 | } 321 | 322 | if(*nwrite > 0){ 323 | statistic->OnWrite(context->GetId(), *nwrite); 324 | } 325 | 326 | return ret; 327 | } 328 | 329 | int StSocket::Close(){ 330 | int ret = ERROR_SUCCESS; 331 | 332 | if(sock_nfd == NULL){ 333 | return ret; 334 | } 335 | 336 | if(st_netfd_close(sock_nfd) != 0){ 337 | ret = ERROR_CLOSE; 338 | } 339 | 340 | sock_nfd = NULL; 341 | status = SocketDisconnected; 342 | 343 | return ret; 344 | } 345 | 346 | int64_t StUtility::GetCurrentTime(){ 347 | timeval now; 348 | 349 | int ret = gettimeofday(&now, NULL); 350 | 351 | if(ret == -1){ 352 | Warn("gettimeofday error, ret=%d", ret); 353 | } 354 | 355 | // we must convert the tv_sec/tv_usec to int64_t. 356 | return ((int64_t)now.tv_sec)*1000 + ((int64_t)now.tv_usec) / 1000; 357 | } 358 | 359 | void StUtility::InitRandom(){ 360 | timeval now; 361 | 362 | if(gettimeofday(&now, NULL) == -1){ 363 | srand(0); 364 | return; 365 | } 366 | 367 | srand(now.tv_sec * 1000000 + now.tv_usec); 368 | } 369 | 370 | st_utime_t StUtility::BuildRandomMTime(double sleep_seconds){ 371 | if(sleep_seconds <= 0){ 372 | return 0 * 1000; 373 | } 374 | 375 | // 80% consts value. 376 | // 40% random value. 377 | // to get more graceful random time to mocking HLS client. 378 | st_utime_t sleep_ms = (int)(sleep_seconds * 1000 * 0.7) + rand() % (int)(sleep_seconds * 1000 * 0.4); 379 | 380 | return sleep_ms; 381 | } 382 | 383 | int StUtility::DnsResolve(string host, string& ip){ 384 | int ret = ERROR_SUCCESS; 385 | 386 | if(inet_addr(host.c_str()) != INADDR_NONE){ 387 | ip = host; 388 | Info("dns resolve %s to %s", host.c_str(), ip.c_str()); 389 | 390 | return ret; 391 | } 392 | 393 | hostent* answer = gethostbyname(host.c_str()); 394 | if(answer == NULL){ 395 | ret = ERROR_DNS_RESOLVE; 396 | Error("dns resolve host %s error. ret=%d", host.c_str(), ret); 397 | return ret; 398 | } 399 | 400 | char ipv4[16]; 401 | memset(ipv4, 0, sizeof(ipv4)); 402 | for(int i = 0; i < answer->h_length; i++){ 403 | inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4)); 404 | Info("dns resolve host %s to %s.", host.c_str(), ipv4); 405 | break; 406 | } 407 | 408 | ip = ipv4; 409 | Info("dns resolve %s to %s", host.c_str(), ip.c_str()); 410 | 411 | return ret; 412 | } 413 | 414 | StLogContext::StLogContext(){ 415 | } 416 | 417 | StLogContext::~StLogContext(){ 418 | } 419 | 420 | void StLogContext::SetId(int id){ 421 | cache[st_thread_self()] = id; 422 | } 423 | 424 | int StLogContext::GetId(){ 425 | return cache[st_thread_self()]; 426 | } 427 | 428 | -------------------------------------------------------------------------------- /src/os/htl_os_st.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _htl_os_st_hpp 2 | #define _htl_os_st_hpp 3 | 4 | /* 5 | #include <htl_os_st.hpp> 6 | */ 7 | 8 | #include <map> 9 | #include <string> 10 | 11 | #include <st.h> 12 | 13 | #include <htl_core_log.hpp> 14 | 15 | // the statistic for each st-thread(task) 16 | class StStatistic 17 | { 18 | private: 19 | int threads, alive; 20 | int64_t starttime, task_duration; 21 | int64_t nread, nwrite; 22 | int64_t tasks, err_tasks, sub_tasks, err_sub_tasks; 23 | public: 24 | StStatistic(); 25 | virtual ~StStatistic(); 26 | public: 27 | virtual void OnRead(int tid, ssize_t nread_bytes); 28 | virtual void OnWrite(int tid, ssize_t nwrite_bytes); 29 | public: 30 | // when task thread run. 31 | virtual void OnThreadRun(int tid); 32 | // when task thread quit. 33 | virtual void OnThreadQuit(int tid); 34 | // when task start request url, ie. get m3u8 35 | virtual void OnTaskStart(int tid, std::string task_url); 36 | // when task error. 37 | virtual void OnTaskError(int tid, int duration_seconds); 38 | // when task finish request url, ie. finish all ts in m3u8 39 | virtual void OnTaskEnd(int tid, int duration_seconds); 40 | // when sub task start, ie. get ts in m3u8 41 | virtual void OnSubTaskStart(int tid, std::string sub_task_url); 42 | // when sub task error. 43 | virtual void OnSubTaskError(int tid, int duration_seconds); 44 | // when sub task finish, ie. finish a ts. 45 | virtual void OnSubTaskEnd(int tid, int duration_seconds); 46 | public: 47 | virtual void DoReport(double sleep_ms); 48 | }; 49 | 50 | extern StStatistic* statistic; 51 | 52 | // abstract task for st, which run in a st-thread. 53 | class StTask 54 | { 55 | private: 56 | int id; 57 | public: 58 | StTask(); 59 | virtual ~StTask(); 60 | public: 61 | virtual int GetId(); 62 | public: 63 | /** 64 | * the framework will start a thread for the task, 65 | * then invoke the Process function to do actual transaction. 66 | */ 67 | virtual int Process() = 0; 68 | }; 69 | 70 | // the farm for all StTask, to spawn st-thread and process all task. 71 | class StFarm 72 | { 73 | private: 74 | double report_seconds; 75 | public: 76 | StFarm(); 77 | virtual ~StFarm(); 78 | public: 79 | virtual int Initialize(double report); 80 | virtual int Spawn(StTask* task); 81 | virtual int WaitAll(); 82 | private: 83 | static void* st_thread_function(void* args); 84 | }; 85 | 86 | // the socket status 87 | enum SocketStatus{ 88 | SocketInit, 89 | SocketConnected, 90 | SocketDisconnected, 91 | }; 92 | 93 | // the socket base on st. 94 | class StSocket 95 | { 96 | private: 97 | SocketStatus status; 98 | st_netfd_t sock_nfd; 99 | public: 100 | StSocket(); 101 | virtual ~StSocket(); 102 | public: 103 | virtual st_netfd_t GetStfd(); 104 | virtual SocketStatus Status(); 105 | virtual int Connect(const char* ip, int port); 106 | virtual int Read(const void* buf, size_t size, ssize_t* nread); 107 | virtual int ReadFully(const void* buf, size_t size, ssize_t* nread); 108 | virtual int Write(const void* buf, size_t size, ssize_t* nwrite); 109 | virtual int Close(); 110 | }; 111 | 112 | // common utilities. 113 | class StUtility 114 | { 115 | public: 116 | static int64_t GetCurrentTime(); 117 | static void InitRandom(); 118 | static st_utime_t BuildRandomMTime(double sleep_seconds); 119 | static int DnsResolve(std::string host, std::string& ip); 120 | }; 121 | 122 | // st-thread based log context. 123 | class StLogContext : public LogContext 124 | { 125 | private: 126 | std::map<st_thread_t, int> cache; 127 | public: 128 | StLogContext(); 129 | virtual ~StLogContext(); 130 | public: 131 | virtual void SetId(int id); 132 | virtual int GetId(); 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/st-load/init: -------------------------------------------------------------------------------- 1 | #ifndef _upp_icpp_init_stub 2 | #define _upp_icpp_init_stub 3 | #endif 4 | -------------------------------------------------------------------------------- /src/st-load/st-load.upp: -------------------------------------------------------------------------------- 1 | file 2 | main readonly separator, 3 | ..\main\htl_main_hls_load.cpp, 4 | ..\main\htl_main_http_load.cpp, 5 | ..\main\htl_main_rtmp_load.cpp, 6 | ..\main\htl_main_utility.cpp, 7 | ..\main\htl_main_utility.hpp, 8 | app readonly separator, 9 | ..\app\htl_app_hls_load.cpp, 10 | ..\app\htl_app_hls_load.hpp, 11 | ..\app\htl_app_m3u8_parser.cpp, 12 | ..\app\htl_app_m3u8_parser.hpp, 13 | ..\app\htl_app_rtmp_load.cpp, 14 | ..\app\htl_app_rtmp_load.hpp, 15 | ..\app\htl_app_rtmp_client.cpp, 16 | ..\app\htl_app_rtmp_client.hpp, 17 | ..\app\htl_app_rtmp_protocol.cpp, 18 | ..\app\htl_app_rtmp_protocol.hpp, 19 | ..\app\htl_app_http_load.cpp, 20 | ..\app\htl_app_http_load.hpp, 21 | ..\app\htl_app_task_base.cpp, 22 | ..\app\htl_app_task_base.hpp, 23 | ..\app\htl_app_http_client.cpp, 24 | ..\app\htl_app_http_client.hpp, 25 | core readonly separator, 26 | ..\core\htl_stdinc.hpp, 27 | ..\core\htl_core_error.cpp, 28 | ..\core\htl_core_error.hpp, 29 | ..\core\htl_core_log.cpp, 30 | ..\core\htl_core_log.hpp, 31 | ..\core\htl_core_uri.cpp, 32 | ..\core\htl_core_uri.hpp, 33 | ..\core\htl_core_aggregate_ret.cpp, 34 | ..\core\htl_core_aggregate_ret.hpp, 35 | os readonly separator, 36 | ..\os\htl_os_st.cpp, 37 | ..\os\htl_os_st.hpp, 38 | st-1.9 readonly separator, 39 | ..\..\objs\st-1.9\obj\st.h, 40 | http-parser readonly separator, 41 | ..\..\objs\http-parser-2.1\http_parser.h, 42 | ..\..\objs\http-parser-2.1\http_parser.c; 43 | 44 | mainconfig 45 | "main" = "MAIN"; 46 | 47 | 48 | -------------------------------------------------------------------------------- /start_hls_live.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cpu_id=`ps aux|grep st|grep hls|grep load|wc -l` 3 | 4 | ./objs/st_hls_load --clients=3000 --url=http://127.0.0.1:80/hls/manifest.m3u8 id=${cpu_id} 1>/dev/null 2>>report.log & 5 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "start process failed, ret=${ret}"; exit ${ret}; fi 6 | 7 | pid=`ps aux|grep st|grep hls|grep load|grep id=${cpu_id}|awk '{print $2}'` 8 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "get process pid failed, ret=${ret}"; exit ${ret}; fi 9 | 10 | echo "pid=${pid} cpu_id=$cpu_id" 11 | 12 | sudo taskset -pc ${cpu_id} ${pid} 13 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "bind cpu failed, ret=${ret}"; exit ${ret}; fi 14 | 15 | echo "$pid started bind to ${cpu_id}" 16 | -------------------------------------------------------------------------------- /start_hls_vod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cpu_id=`ps aux|grep st|grep hls|grep load|wc -l` 3 | 4 | ./objs/st_hls_load --clients=3000 --url=http://127.0.0.1:80/hls/manifest.m3u8 --vod id=${cpu_id} 1>/dev/null 2>>report.log & 5 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "start process failed, ret=${ret}"; exit ${ret}; fi 6 | 7 | pid=`ps aux|grep st|grep hls|grep load|grep id=${cpu_id}|awk '{print $2}'` 8 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "get process pid failed, ret=${ret}"; exit ${ret}; fi 9 | 10 | echo "pid=${pid} cpu_id=$cpu_id" 11 | 12 | sudo taskset -pc ${cpu_id} ${pid} 13 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "bind cpu failed, ret=${ret}"; exit ${ret}; fi 14 | 15 | echo "$pid started bind to ${cpu_id}" 16 | -------------------------------------------------------------------------------- /start_http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cpu_id=`ps aux|grep st|grep http|grep load|wc -l` 3 | 4 | ./objs/st_http_load --clients=3000 --url=http://127.0.0.1:80/hls/manifest.m3u8 id=${cpu_id} 1>/dev/null 2>>report.log & 5 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "start process failed, ret=${ret}"; exit ${ret}; fi 6 | 7 | pid=`ps aux|grep st|grep http|grep load|grep id=${cpu_id}|awk '{print $2}'` 8 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "get process pid failed, ret=${ret}"; exit ${ret}; fi 9 | 10 | echo "pid=${pid} cpu_id=$cpu_id" 11 | 12 | sudo taskset -pc ${cpu_id} ${pid} 13 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "bind cpu failed, ret=${ret}"; exit ${ret}; fi 14 | 15 | echo "$pid started bind to ${cpu_id}" 16 | -------------------------------------------------------------------------------- /start_rtmp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cpu_id=`ps aux|grep st|grep rtmp|grep load|wc -l` 3 | 4 | ./objs/st_rtmp_load --clients=3000 --url=rtmp://127.0.0.1:1935/live/livestream id=${cpu_id} 1>/dev/null 2>>report.log & 5 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "start process failed, ret=${ret}"; exit ${ret}; fi 6 | 7 | pid=`ps aux|grep st|grep rtmp|grep load|grep id=${cpu_id}|awk '{print $2}'` 8 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "get process pid failed, ret=${ret}"; exit ${ret}; fi 9 | 10 | echo "pid=${pid} cpu_id=$cpu_id" 11 | 12 | sudo taskset -pc ${cpu_id} ${pid} 13 | ret=$?; if [[ 0 -ne ${ret} ]];then echo "bind cpu failed, ret=${ret}"; exit ${ret}; fi 14 | 15 | echo "$pid started bind to ${cpu_id}" 16 | --------------------------------------------------------------------------------