first commit
commit
3c34909cb5
|
@ -0,0 +1,12 @@
|
||||||
|
FROM gcc:5.4
|
||||||
|
|
||||||
|
RUN apt update && apt install -y zip
|
||||||
|
|
||||||
|
COPY st-load-master src
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
RUN ./configure
|
||||||
|
RUN make
|
||||||
|
|
||||||
|
WORKDIR /src/objs
|
||||||
|
ENV PATH="/src/objs:${PATH}"
|
|
@ -0,0 +1,22 @@
|
||||||
|
# rtmp-stress
|
||||||
|
|
||||||
|
> clone from https://github.com/rzrobert/st-load-master
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker build -t rtmp-stress .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# start
|
||||||
|
docker run -d --name stress rtmp-stress sb_rtmp_load_fast -c 50 -r rtmp://kumoly.io/mosu/live
|
||||||
|
|
||||||
|
# see the logs
|
||||||
|
docker logs -f stress
|
||||||
|
|
||||||
|
# stop
|
||||||
|
docker rm -f stress
|
||||||
|
```
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
Binary file not shown.
|
@ -0,0 +1,21 @@
|
||||||
|
Only in .: 1.st.arm.patch
|
||||||
|
diff -r -c ./md.h ../st-1.9-patch-arm/md.h
|
||||||
|
*** ./md.h 2009-10-02 02:46:43.000000000 +0800
|
||||||
|
--- ../st-1.9-patch-arm/md.h 2014-03-16 20:49:03.845344804 +0800
|
||||||
|
***************
|
||||||
|
*** 422,428 ****
|
||||||
|
#define MD_STACK_GROWS_DOWN
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && __GLIBC__ >= 2
|
||||||
|
! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[20]
|
||||||
|
#else
|
||||||
|
#error "ARM/Linux pre-glibc2 not supported yet"
|
||||||
|
#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
|
||||||
|
--- 422,428 ----
|
||||||
|
#define MD_STACK_GROWS_DOWN
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && __GLIBC__ >= 2
|
||||||
|
! #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8]
|
||||||
|
#else
|
||||||
|
#error "ARM/Linux pre-glibc2 not supported yet"
|
||||||
|
#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
|
Binary file not shown.
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 srs-org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,127 @@
|
||||||
|
SB(SRS Bench)
|
||||||
|
===========
|
||||||
|
|
||||||
|
hls/http/rtmp-play/rtmp-publish load test tool base on st(state-threads), support huge concurrency<br/>
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
服务器负载测试工具SB(SRS Bench):
|
||||||
|
|
||||||
|
1. 模拟huge并发:2G内存就可以开300k连接。基于states-threads的协程。
|
||||||
|
1. 支持HLS解析和测试,下载ts片后等待一个切片长度,模拟客户端。支持HLS点播和直播。执行程序:`./objs/sb_hls_load`
|
||||||
|
1. 支持HTTP负载测试,所有并发重复下载一个http文件。可将80Gbps带宽测试的72Gbps。执行程序:`./objs/sb_http_load `
|
||||||
|
1. 支持RTMP流播放测试,一个进程支持5k并发。执行程序:`./objs/sb_rtmp_load`
|
||||||
|
1. 支持RTMP流推流测试,一个进程支持500个并发。执行程序:`./objs/sb_rtmp_publish`
|
||||||
|
1. RTMP协议使用高性能服务器SRS([SimpleRtmpServer](https://github.com/winlinvip/simple-rtmp-server))的协议栈。
|
||||||
|
|
||||||
|
注意:
|
||||||
|
|
||||||
|
1. HTTP/HLS:依赖服务器Content-Length,不支持chunked方式(chunked时会把所有内容当做body一直读)。
|
||||||
|
2. 所有程序都在Linux下运行,模拟客户端运行。
|
||||||
|
3. 其他工具参考[srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#srs-librtmp-examples)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/winlinvip/st-load.git &&
|
||||||
|
cd st-load && ./configure && make &&
|
||||||
|
./objs/sb_rtmp_load -c 1 -r rtmp://127.0.0.1:1935/live/livestream
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
TestEnvironment: 24CPU, 80Gbps Network, 16GB Memory<br/>
|
||||||
|
Server: NGINX HLS<br/>
|
||||||
|
Result: 90% bandwith, 72Gbps
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
[root@dell-server ~]# dstat
|
||||||
|
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
|
||||||
|
usr sys idl wai hiq siq| read writ| recv send | in out | int csw
|
||||||
|
1 1 95 0 0 3|4091B 369k| 0 0 | 0 0 | 100k 9545
|
||||||
|
3 8 66 0 0 23| 0 0 | 40MB 6114MB| 0 0 | 681k 46k
|
||||||
|
3 8 63 0 0 25| 0 100k| 41MB 6223MB| 0 0 | 692k 46k
|
||||||
|
3 8 64 0 0 25| 0 0 | 41MB 6190MB| 0 0 | 694k 45k
|
||||||
|
3 8 66 0 0 23| 0 0 | 40MB 6272MB| 0 0 | 694k 48k
|
||||||
|
3 8 64 0 0 25| 0 20k| 40MB 6161MB| 0 0 | 687k 46k
|
||||||
|
3 8 65 0 0 24| 0 0 | 40MB 6198MB| 0 0 | 687k 46k
|
||||||
|
3 8 66 0 0 23| 0 0 | 40MB 6231MB| 0 0 | 688k 47k
|
||||||
|
3 7 70 0 0 20| 0 68k| 40MB 6159MB| 0 0 | 675k 49k
|
||||||
|
3 9 62 0 0 26| 0 4096B| 42MB 6283MB| 0 0 | 702k 44k
|
||||||
|
4 8 62 0 0 25| 0 2472k| 40MB 6122MB| 0 0 | 698k 44k
|
||||||
|
3 8 67 0 0 22| 0 0 | 39MB 6066MB| 0 0 | 671k 46k
|
||||||
|
3 8 64 0 0 25| 0 0 | 41MB 6263MB| 0 0 | 695k 46k
|
||||||
|
3 8 64 0 0 25|4096B 132k| 41MB 6161MB| 0 0 | 687k 45k
|
||||||
|
3 11 60 0 0 26| 0 0 | 42MB 6822MB| 0 0 | 714k 36k
|
||||||
|
3 10 62 0 0 25| 0 0 | 40MB 6734MB| 0 0 | 703k 38k
|
||||||
|
3 11 60 0 0 26| 0 0 | 43MB 7019MB| 0 0 | 724k 38k
|
||||||
|
3 11 60 0 0 26| 0 24k| 45MB 7436MB| 0 0 | 746k 41k
|
||||||
|
3 11 60 0 0 27| 0 24k| 47MB 7736MB| 0 0 | 766k 42k
|
||||||
|
3 11 59 0 0 28| 0 0 | 52MB 8283MB| 0 0 | 806k 45k
|
||||||
|
2 10 61 0 0 27| 0 0 | 54MB 8359MB| 0 0 | 806k 47k
|
||||||
|
3 12 53 0 0 32| 0 16k| 58MB 8565MB| 0 0 | 850k 42k
|
||||||
|
2 10 62 0 0 26| 0 1212k| 51MB 8140MB| 0 0 | 783k 47k
|
||||||
|
2 10 64 0 0 24| 0 0 | 42MB 7033MB| 0 0 | 703k 40k
|
||||||
|
2 11 62 0 0 25| 0 0 | 43MB 7203MB| 0 0 | 703k 40k
|
||||||
|
2 12 57 0 0 29| 0 0 | 50MB 7970MB| 0 0 | 774k 40k
|
||||||
|
2 11 54 0 0 33| 0 0 | 72MB 8943MB| 0 0 | 912k 47k
|
||||||
|
3 13 65 0 0 20| 0 0 | 36MB 7247MB| 0 0 | 552k 29k
|
||||||
|
3 14 61 0 0 23| 0 1492k| 42MB 8091MB| 0 0 | 613k 32k
|
||||||
|
3 13 54 0 0 30| 0 0 | 57MB 9144MB| 0 0 | 760k 34k
|
||||||
|
2 10 55 0 0 32| 0 84k| 69MB 9292MB| 0 0 | 861k 38k
|
||||||
|
2 9 58 0 0 31| 0 92k| 71MB 9083MB| 0 0 | 860k 39k
|
||||||
|
2 9 56 0 0 33| 0 0 | 78MB 9098MB| 0 0 | 914k 39k
|
||||||
|
2 8 61 0 0 30| 0 0 | 73MB 8860MB| 0 0 | 876k 39k
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
RTMP load test:<br/>
|
||||||
|
<pre>
|
||||||
|
top - 17:57:24 up 7:10, 7 users, load average: 0.20, 0.20, 0.09
|
||||||
|
Tasks: 154 total, 1 running, 153 sleeping, 0 stopped, 0 zombie
|
||||||
|
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
|
||||||
|
Mem: 2055440k total, 1304528k used, 750912k free, 182336k buffers
|
||||||
|
Swap: 2064376k total, 0k used, 2064376k free, 613848k cached
|
||||||
|
|
||||||
|
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||||
|
13091 winlin 20 0 186m 110m 1404 S 29.6 5.5 1:55.35 ./objs/sb_rtmp_load -c 1000
|
||||||
|
12544 winlin 20 0 124m 22m 2080 S 20.3 1.1 1:51.51 ./objs/srs
|
||||||
|
|
||||||
|
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
|
||||||
|
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
|
||||||
|
7 7 82 0 0 4| 0 0 | 158M 158M| 0 0 |2962 353
|
||||||
|
6 5 83 0 0 6| 0 0 | 74M 74M| 0 0 |2849 291
|
||||||
|
7 6 81 0 0 6| 0 0 | 102M 102M| 0 0 |2966 360
|
||||||
|
7 8 79 0 0 6| 0 0 | 168M 168M| 0 0 |2889 321
|
||||||
|
7 7 79 0 0 7| 0 0 | 83M 83M| 0 0 |2862 364
|
||||||
|
5 6 83 0 0 6| 0 0 | 106M 106M| 0 0 |2967 296
|
||||||
|
5 6 83 0 0 6| 0 0 | 54M 54M| 0 0 |2907 355
|
||||||
|
6 6 84 0 0 4| 0 0 | 58M 58M| 0 0 |2986 353
|
||||||
|
6 6 83 0 0 4| 0 0 | 117M 117M| 0 0 |2863 326
|
||||||
|
7 6 82 0 0 5| 0 0 | 97M 97M| 0 0 |2954 321
|
||||||
|
5 7 78 2 0 8| 0 40k| 82M 82M| 0 0 |2909 357
|
||||||
|
5 5 84 0 0 6| 0 0 | 57M 57M| 0 0 |2937 307
|
||||||
|
8 8 78 0 0 6| 0 0 | 190M 190M| 0 0 |3024 413
|
||||||
|
5 7 82 0 0 7| 0 0 | 75M 75M| 0 0 |2940 310
|
||||||
|
8 8 80 0 0 4| 0 0 | 136M 136M| 0 0 |3000 436
|
||||||
|
8 8 74 0 0 10| 0 0 | 116M 116M| 0 0 |2816 356
|
||||||
|
7 8 78 0 0 6| 0 0 | 128M 128M| 0 0 |2972 424
|
||||||
|
6 8 80 0 0 7| 0 4096B| 123M 123M| 0 0 |2981 395
|
||||||
|
6 6 83 0 0 5| 0 0 | 50M 50M| 0 0 |2984 367
|
||||||
|
7 6 81 2 0 4| 0 92k| 49M 49M| 0 0 |3010 445
|
||||||
|
5 6 84 0 0 6| 0 0 | 22M 22M| 0 0 |2912 364
|
||||||
|
5 5 85 0 0 4| 0 0 | 34M 34M| 0 0 |3001 429
|
||||||
|
6 6 81 0 0 7| 0 0 | 45M 45M| 0 0 |2996 468
|
||||||
|
5 5 84 0 0 6| 0 0 | 18M 18M| 0 0 |2923 338
|
||||||
|
8 8 77 0 0 7| 0 0 | 158M 158M| 0 0 |2971 351
|
||||||
|
7 7 80 0 0 5| 0 0 | 167M 167M| 0 0 |2860 334
|
||||||
|
6 5 83 0 0 6| 0 60k| 61M 61M| 0 0 |2988 424
|
||||||
|
7 8 79 0 0 6| 0 0 | 140M 140M| 0 0 |2916 391
|
||||||
|
8 8 78 0 0 6| 0 0 | 172M 172M| 0 0 |2961 348
|
||||||
|
7 8 78 0 0 7| 0 0 | 127M 127M| 0 0 |2865 347
|
||||||
|
5 6 84 0 0 5| 0 0 | 73M 73M| 0 0 |2972 344
|
||||||
|
6 8 78 0 0 8| 0 0 | 115M 115M| 0 0 |2942 314
|
||||||
|
7 8 79 0 0 6| 0 0 | 147M 147M| 0 0 |2966 366
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
2016-12-01
|
||||||
|
</pre>
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# params:
|
||||||
|
# $GLOBAL_DIR_OBJS the objs directory. ie. objs
|
||||||
|
# $GLOBAL_FILE_MAKEFILE the makefile name. ie. Makefile
|
||||||
|
# $MAIN_ENTRANCES array, all main entrance, disable all except the $APP_MAIN itself
|
||||||
|
# $MODULE_OBJS array, the objects to compile the app.
|
||||||
|
# $BUILD_KEY a string indicates the build key for Makefile. ie. dump
|
||||||
|
# $APP_MAIN the object file that contains main function. ie. your_app_main
|
||||||
|
# $APP_NAME the app name to output. ie. your_app
|
||||||
|
# $ModuleLibFiles array, the 3rdpart library file to link with. ie. (objs/st-1.9/obj/libst.a objs/libx264/obj/libx264.a)
|
||||||
|
# $LINK_OPTIONS the linker options.
|
||||||
|
# $SO_PATH the libssl.so.10 and other so file path.
|
||||||
|
|
||||||
|
FILE=${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE}
|
||||||
|
|
||||||
|
APP_TARGET="${GLOBAL_DIR_OBJS}/${APP_NAME}"
|
||||||
|
|
||||||
|
echo "generate app ${APP_NAME} depends...";
|
||||||
|
|
||||||
|
echo "# build ${APP_TARGET}" >> ${FILE}
|
||||||
|
echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE}
|
||||||
|
|
||||||
|
echo -n "${APP_TARGET}: " >> ${FILE}
|
||||||
|
for item in ${MODULE_OBJS[*]}; do
|
||||||
|
FILE_NAME=`basename $item`
|
||||||
|
FILE_NAME=${FILE_NAME%.*}
|
||||||
|
|
||||||
|
ignored=0
|
||||||
|
for disabled_item in ${MAIN_ENTRANCES[*]}; do
|
||||||
|
if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then
|
||||||
|
ignored=1
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -f ${item} ]; then
|
||||||
|
ignored=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ignored} == 1 ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
OBJ_FILE=${GLOBAL_DIR_OBJS}/$item
|
||||||
|
OBJ_FILE="${OBJ_FILE%.*}.o"
|
||||||
|
echo -n "${OBJ_FILE} " >> ${FILE}
|
||||||
|
done
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
|
echo "generate app ${APP_NAME} link...";
|
||||||
|
|
||||||
|
echo -n " \$(LINK) ${PerformanceLink} -o ${APP_TARGET} " >> ${FILE}
|
||||||
|
for item in ${MODULE_OBJS[*]}; do
|
||||||
|
FILE_NAME=`basename $item`
|
||||||
|
FILE_NAME=${FILE_NAME%.*}
|
||||||
|
|
||||||
|
ignored=0
|
||||||
|
for disabled_item in ${MAIN_ENTRANCES[*]}; do
|
||||||
|
if [[ ${FILE_NAME} == ${disabled_item} && ${FILE_NAME} != ${APP_MAIN} ]]; then
|
||||||
|
ignored=1
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -f ${item} ]; then
|
||||||
|
ignored=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${ignored} == 1 ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
OBJ_FILE=${GLOBAL_DIR_OBJS}/$item
|
||||||
|
OBJ_FILE="${OBJ_FILE%.*}.o"
|
||||||
|
echo -n "${OBJ_FILE} " >> ${FILE}
|
||||||
|
done
|
||||||
|
# 3rdpart library static link.
|
||||||
|
for item in ${ModuleLibFiles[*]}; do
|
||||||
|
echo -n "$item " >> ${FILE}
|
||||||
|
done
|
||||||
|
# link options.
|
||||||
|
echo -n "${LINK_OPTIONS}" >> ${FILE}
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
|
# set the so reference path.
|
||||||
|
if [[ ! -z ${SO_PATH} ]]; then
|
||||||
|
echo -n " @bash auto/set_so_rpath.sh ${SOPathTool} ${APP_TARGET} ${SO_PATH}" >> ${FILE}
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "generate app ${APP_NAME} ok"; echo '!';
|
|
@ -0,0 +1,61 @@
|
||||||
|
# params:
|
||||||
|
# $GLOBAL_DIR_OBJS the objs directory. ie. objs
|
||||||
|
# $GLOBAL_FILE_MAKEFILE the makefile name. ie. Makefile
|
||||||
|
# $MODULE_DIR the module dir. ie. src/os/linux
|
||||||
|
# $MODULE_ID the id of module. ie. CORE
|
||||||
|
# $MODULE_DEPENDS array, the denpend MODULEs id. ie. (CORE OS)
|
||||||
|
# $ModuleLibIncs array, the depend 3rdpart library includes. ie. (objs/st-1.9/obj objs/libx264/obj)
|
||||||
|
# $MODULE_FILES array, the head/cpp files of modules. ie. (public log)
|
||||||
|
#
|
||||||
|
# returns:
|
||||||
|
# $MODULE_OBJS array, the objects of the modules.
|
||||||
|
|
||||||
|
FILE=${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE}
|
||||||
|
|
||||||
|
# INCS
|
||||||
|
INCS_NAME="${MODULE_ID}_INCS"
|
||||||
|
echo "# the ${MODULE_ID} module." >> ${FILE}
|
||||||
|
echo "${MODULE_ID}_MODULE_INCS = -I ${MODULE_DIR} " >> ${FILE}
|
||||||
|
echo -n "${INCS_NAME} = -I ${MODULE_DIR} " >> ${FILE}
|
||||||
|
for item in ${MODULE_DEPENDS[*]}; do
|
||||||
|
DEP_INCS_NAME="${item}_INCS"do
|
||||||
|
DEP_INCS_NAME="${item}_MODULE_INCS"
|
||||||
|
echo -n "\$(${DEP_INCS_NAME}) " >> ${FILE}
|
||||||
|
done
|
||||||
|
for item in ${ModuleLibIncs[*]}; do
|
||||||
|
echo -n "-I ${item} " >> ${FILE}
|
||||||
|
done
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
|
# DEPS
|
||||||
|
DEPS_NAME="${MODULE_ID}_DEPS"
|
||||||
|
echo -n "${DEPS_NAME} = " >> ${FILE}
|
||||||
|
for item in ${MODULE_FILES[*]}; do
|
||||||
|
HEADER_FILE="${MODULE_DIR}/${item}.hpp"
|
||||||
|
if [ -f ${HEADER_FILE} ]; then
|
||||||
|
echo -n " ${HEADER_FILE}" >> ${FILE}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for item in ${MODULE_DEPENDS[*]}; do
|
||||||
|
DEP_DEPS_NAME="${item}_DEPS"
|
||||||
|
echo -n " \$(${DEP_DEPS_NAME}) " >> ${FILE}
|
||||||
|
done
|
||||||
|
echo "" >> ${FILE}; echo "" >> ${FILE}
|
||||||
|
|
||||||
|
# OBJ
|
||||||
|
MODULE_OBJS=()
|
||||||
|
for item in ${MODULE_FILES[*]}; do
|
||||||
|
CPP_FILE="${MODULE_DIR}/${item}.cpp"
|
||||||
|
OBJ_FILE="${GLOBAL_DIR_OBJS}/${MODULE_DIR}/${item}.o"
|
||||||
|
MODULE_OBJS="${MODULE_OBJS[@]} ${CPP_FILE}"
|
||||||
|
if [ -f ${CPP_FILE} ]; then
|
||||||
|
echo "${OBJ_FILE}: \$(${DEPS_NAME}) ${CPP_FILE} " >> ${FILE}
|
||||||
|
echo " \$(GCC) -c \$(CXXFLAGS) \$(${INCS_NAME}) -o ${OBJ_FILE} ${CPP_FILE}" >> ${FILE}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
|
# Makefile
|
||||||
|
echo " mkdir -p ${GLOBAL_DIR_OBJS}/${MODULE_DIR}" >> ${GLOBAL_FILE_MAKEFILE}
|
||||||
|
|
||||||
|
echo -n "generate module ${MODULE_ID} ok"; echo '!';
|
|
@ -0,0 +1,215 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
GLOBAL_FILE_MAKEFILE="Makefile"
|
||||||
|
GLOBAL_DIR_OBJS="objs"
|
||||||
|
|
||||||
|
mkdir -p ${GLOBAL_DIR_OBJS}
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# prepare the depends tools
|
||||||
|
#####################################################################################
|
||||||
|
OS_IS_OSX=NO
|
||||||
|
uname -s|grep Darwin >/dev/null 2>&1
|
||||||
|
ret=$?; if [[ 0 -eq $ret ]]; then
|
||||||
|
OS_IS_OSX=YES
|
||||||
|
fi
|
||||||
|
echo "Is OSX: ${OS_IS_OSX}"
|
||||||
|
|
||||||
|
# check the arm flag file, if flag changed, need to rebuild the st.
|
||||||
|
_ST_MAKE=linux-debug && _ST_EXTRA_CFLAGS="-DMALLOC_STACK -DMD_HAVE_EPOLL"
|
||||||
|
# for osx, use darwin for st, donot use epoll.
|
||||||
|
if [ $OS_IS_OSX = YES ]; then
|
||||||
|
_ST_MAKE=darwin-debug && _ST_EXTRA_CFLAGS="-DMD_HAVE_KQUEUE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# st-1.9
|
||||||
|
if [[ -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.a ]]; then
|
||||||
|
echo "st-1.9t is ok.";
|
||||||
|
else
|
||||||
|
echo "build st-1.9t";
|
||||||
|
(
|
||||||
|
rm -rf ${GLOBAL_DIR_OBJS}/st-1.9 && cd ${GLOBAL_DIR_OBJS} && unzip ../3rdparty/st-1.9.zip && cd st-1.9 &&
|
||||||
|
patch -p0 < ../../3rdparty/patches/1.st.arm.patch &&
|
||||||
|
make EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}" ${_ST_MAKE}
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
# check status
|
||||||
|
ret=$?; if [[ $ret -ne 0 ]]; then echo "build st-1.9 failed, ret=$ret"; exit $ret; fi
|
||||||
|
if [ ! -f ${GLOBAL_DIR_OBJS}/st-1.9/obj/libst.a ]; then echo "build st-1.9 failed."; exit -1; fi
|
||||||
|
|
||||||
|
SED="sed -i"
|
||||||
|
if [ $OS_IS_OSX = YES ]; then SED="sed -i ''"; fi
|
||||||
|
|
||||||
|
# http-parser-2.1
|
||||||
|
if [[ -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/http_parser.h && -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then
|
||||||
|
echo "http-parser-2.1 is ok.";
|
||||||
|
else
|
||||||
|
echo "build http-parser-2.1";
|
||||||
|
(
|
||||||
|
rm -rf ${GLOBAL_DIR_OBJS}/http-parser-2.1 && cd ${GLOBAL_DIR_OBJS} && unzip ../3rdparty/http-parser-2.1.zip &&
|
||||||
|
cd http-parser-2.1 &&
|
||||||
|
$SED "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile &&
|
||||||
|
$SED "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile &&
|
||||||
|
make package
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
# check status
|
||||||
|
ret=$?; if [[ $ret -ne 0 ]]; then echo "build http-parser-2.1 failed, ret=$ret"; exit $ret; fi
|
||||||
|
if [[ ! -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/http_parser.h ]]; then echo "build http-parser-2.1 failed"; exit -1; fi
|
||||||
|
if [[ ! -f ${GLOBAL_DIR_OBJS}/http-parser-2.1/libhttp_parser.a ]]; then echo "build http-parser-2.1 failed"; exit -1; fi
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# generate Makefile.
|
||||||
|
#####################################################################################
|
||||||
|
echo "generate Makefile"
|
||||||
|
|
||||||
|
cat << END > ${GLOBAL_FILE_MAKEFILE}
|
||||||
|
.PHONY: default help clean http hls all _prepare_dir
|
||||||
|
default: all
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Usage: make <help>|<clean>|<http>|<hls>|<rtmp>|<all>"
|
||||||
|
@echo " help display this help menu"
|
||||||
|
@echo " clean cleanup project"
|
||||||
|
@echo " http build the http load test tool over st(state-threads)"
|
||||||
|
@echo " hls build the hls load test tool over st(state-threads)"
|
||||||
|
@echo " rtmp build the rtmp load test tool over st(state-threads)"
|
||||||
|
@echo " all build the http/hls load test tool over st(state-threads)"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
(cd ${GLOBAL_DIR_OBJS}; rm -rf src sb_*_load)
|
||||||
|
|
||||||
|
http: _prepare_dir
|
||||||
|
@echo "build the http load test tool over st(state-threads)"
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_http_load
|
||||||
|
|
||||||
|
rtmp: _prepare_dir
|
||||||
|
@echo "build the http load test tool over st(state-threads)"
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_load
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_load_fast
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_publish
|
||||||
|
|
||||||
|
hls: _prepare_dir
|
||||||
|
@echo "build the HLS load test tool over st(state-threads)"
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_hls_load
|
||||||
|
|
||||||
|
all: _prepare_dir
|
||||||
|
@echo "build the http/hls/rtmp load test tool over st(state-threads)"
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_http_load
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_hls_load
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_load
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_load_fast
|
||||||
|
\$(MAKE) -f ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE} sb_rtmp_publish
|
||||||
|
@echo "build ok, you can:"
|
||||||
|
@echo " ./objs/sb_http_load"
|
||||||
|
@echo " ./objs/sb_hls_load"
|
||||||
|
@echo " ./objs/sb_rtmp_load"
|
||||||
|
@echo " ./objs/sb_rtmp_load_fast"
|
||||||
|
@echo " ./objs/sb_rtmp_publish"
|
||||||
|
|
||||||
|
# the ./configure will generate it.
|
||||||
|
_prepare_dir:
|
||||||
|
END
|
||||||
|
|
||||||
|
echo 'generate Makefile ok!'
|
||||||
|
|
||||||
|
# the performance analysis, uncomments the following when use gperf to analysis the performance. see third-party/readme.txt
|
||||||
|
#Performance="-pg"
|
||||||
|
#PerformanceLink="-pg"
|
||||||
|
# enable gdb debug
|
||||||
|
GDBDebug="-g -O0"
|
||||||
|
# the warning level.
|
||||||
|
WarnLevel="-Wall -Wextra"
|
||||||
|
# the compile standard.
|
||||||
|
CppStd="-std=c++98"
|
||||||
|
# other macros defined
|
||||||
|
UserMacros="-DSRS_HIJACK_IO -DSRS_DISABLE_LOG"
|
||||||
|
# the cxx flag generated.
|
||||||
|
CXXFLAGS="${CppStd} ${WarnLevel} ${GDBDebug} ${Performance} ${UserMacros}"
|
||||||
|
cat << END > ${GLOBAL_DIR_OBJS}/${GLOBAL_FILE_MAKEFILE}
|
||||||
|
CXXFLAGS = ${CXXFLAGS}
|
||||||
|
GCC = g++
|
||||||
|
LINK = \$(GCC)
|
||||||
|
AR = ar
|
||||||
|
|
||||||
|
.PHONY: default sb_http_load sb_hls_load sb_rtmp_load sb_rtmp_load_fast sb_rtmp_publish
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
END
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
LibSTRoot="${GLOBAL_DIR_OBJS}/st-1.9/obj"
|
||||||
|
LibSTfile="${LibSTRoot}/libst.a"
|
||||||
|
LibHttpParserRoot="${GLOBAL_DIR_OBJS}/http-parser-2.1"
|
||||||
|
LibHttpParserfile="${LibHttpParserRoot}/libhttp_parser.a"
|
||||||
|
|
||||||
|
#Core Module
|
||||||
|
MODULE_ID="CORE"
|
||||||
|
MODULE_DEPENDS=()
|
||||||
|
ModuleLibIncs=(${LibHttpParserRoot})
|
||||||
|
MODULE_FILES=("htl_core_log" "htl_core_error" "htl_core_uri" "htl_core_aggregate_ret")
|
||||||
|
MODULE_DIR="src/core" . auto/modules.sh
|
||||||
|
CORE_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
|
||||||
|
#OS Module
|
||||||
|
MODULE_ID="OS"
|
||||||
|
MODULE_DEPENDS=("CORE")
|
||||||
|
ModuleLibIncs=(${LibSTRoot})
|
||||||
|
MODULE_FILES=("htl_os_st")
|
||||||
|
MODULE_DIR="src/os" . auto/modules.sh
|
||||||
|
OS_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
|
||||||
|
#APP Module
|
||||||
|
MODULE_ID="APP"
|
||||||
|
MODULE_DEPENDS=("CORE" "OS")
|
||||||
|
ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot})
|
||||||
|
MODULE_FILES=("htl_app_hls_load" "htl_app_http_load" "htl_app_http_client" "htl_app_rtmp_play"
|
||||||
|
"htl_app_m3u8_parser" "htl_app_task_base" "htl_app_rtmp_load" "htl_app_rtmp_protocol"
|
||||||
|
"htl_app_rtmp_publish" "htl_app_srs_hijack")
|
||||||
|
MODULE_DIR="src/app" . auto/modules.sh
|
||||||
|
APP_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
|
||||||
|
#Main Module
|
||||||
|
MODULE_ID="MAIN"
|
||||||
|
MODULE_DEPENDS=("CORE" "OS" "APP")
|
||||||
|
ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot})
|
||||||
|
MODULE_FILES=("htl_main_hls_load" "htl_main_http_load" "htl_main_rtmp_load" "htl_main_rtmp_load_fast" "htl_main_utility" "htl_main_rtmp_publish")
|
||||||
|
MODULE_DIR="src/main" . auto/modules.sh
|
||||||
|
MAIN_OBJS="${MODULE_OBJS[@].o}"
|
||||||
|
|
||||||
|
# all main entrances
|
||||||
|
MAIN_ENTRANCES=("htl_main_hls_load" "htl_main_http_load" "htl_main_rtmp_load" "htl_main_rtmp_load_fast" "htl_main_rtmp_publish")
|
||||||
|
|
||||||
|
# http load test tool over st(state-threads)
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile})
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
BUILD_KEY="sb_http_load" APP_MAIN="htl_main_http_load" APP_NAME="sb_http_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh
|
||||||
|
|
||||||
|
# rtmp play load test tool over st(state-threads)
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile})
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
BUILD_KEY="sb_rtmp_load" APP_MAIN="htl_main_rtmp_load" APP_NAME="sb_rtmp_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh
|
||||||
|
|
||||||
|
# rtmp play load test tool over st(state-threads)
|
||||||
|
# Remark, the fast algorithm may not work for other RTMP server, it's ok for SRS/GO-SRS.
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile})
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
BUILD_KEY="sb_rtmp_load_fast" APP_MAIN="htl_main_rtmp_load_fast" APP_NAME="sb_rtmp_load_fast" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh
|
||||||
|
|
||||||
|
# rtmp publish load test tool over st(state-threads)
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile})
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
BUILD_KEY="sb_rtmp_publish" APP_MAIN="htl_main_rtmp_publish" APP_NAME="sb_rtmp_publish" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh
|
||||||
|
|
||||||
|
# hls load test tool over direct TCP.
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibHttpParserfile})
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${OS_OBJS[@]} ${APP_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
BUILD_KEY="sb_hls_load" APP_MAIN="htl_main_hls_load" APP_NAME="sb_hls_load" LINK_OPTIONS="-ldl" SO_PATH="" . auto/apps.sh
|
||||||
|
|
||||||
|
echo 'configure ok! '
|
||||||
|
|
||||||
|
# next step.
|
||||||
|
echo "you can:"
|
||||||
|
echo "\" make \" to build the http/hls load test tools."
|
||||||
|
echo "\" make help \" to get the usage of make"
|
Binary file not shown.
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_aggregate_ret.hpp>
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_hls_load.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define DEFAULT_TS_DURATION 10
|
||||||
|
|
||||||
|
StHlsTask::StHlsTask(){
|
||||||
|
target_duration = DEFAULT_TS_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
StHlsTask::~StHlsTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHlsTask::Initialize(std::string http_url, bool vod, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
is_vod = vod;
|
||||||
|
|
||||||
|
if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri* StHlsTask::GetUri(){
|
||||||
|
return &url;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHlsTask::ProcessTask(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Trace("start to process HLS task #%d, schema=%s, host=%s, port=%d, path=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d",
|
||||||
|
GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetPath(), startup_seconds, delay_seconds, error_seconds, count);
|
||||||
|
|
||||||
|
StHttpClient client;
|
||||||
|
|
||||||
|
// if count is zero, infinity loop.
|
||||||
|
for(int i = 0; count == 0 || i < count; i++){
|
||||||
|
statistic->OnTaskStart(GetId(), url.GetUrl());
|
||||||
|
|
||||||
|
if((ret = ProcessM3u8(client)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnTaskError(GetId(), 0);
|
||||||
|
|
||||||
|
Error("http client process m3u8 failed. ret=%d", ret);
|
||||||
|
st_usleep((st_utime_t)(error_seconds * 1000 * 1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("[HLS] %s download completed.", url.GetUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHlsTask::ProcessM3u8(StHttpClient& client){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
string m3u8;
|
||||||
|
if((ret = client.DownloadString(&url, &m3u8)) != ERROR_SUCCESS){
|
||||||
|
Error("http client get m3u8 failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Trace("[HLS] get m3u8 %s get success, length=%"PRId64, url.GetUrl(), (int64_t)m3u8.length());
|
||||||
|
|
||||||
|
vector<M3u8TS> ts_objects;
|
||||||
|
if((ret = HlsM3u8Parser::ParseM3u8Data(&url, m3u8, ts_objects, target_duration)) != ERROR_SUCCESS){
|
||||||
|
Error("http client parse m3u8 content failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = ProcessTS(client, ts_objects)) != ERROR_SUCCESS){
|
||||||
|
Error("http client download m3u8 ts file failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHlsTask::ProcessTS(StHttpClient& client, vector<M3u8TS>& ts_objects){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
vector<M3u8TS>::iterator ite = ts_objects.begin();
|
||||||
|
|
||||||
|
// if live(not vod), remember the last download ts object.
|
||||||
|
// if vod(not live), always access from the frist ts.
|
||||||
|
if(!is_vod){
|
||||||
|
ite = find(ts_objects.begin(), ts_objects.end(), last_downloaded_ts);
|
||||||
|
|
||||||
|
// not found, reset to begin to process all.
|
||||||
|
if(ite == ts_objects.end()){
|
||||||
|
ite = ts_objects.begin();
|
||||||
|
}
|
||||||
|
// fount, skip it.
|
||||||
|
else{
|
||||||
|
ite++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no ts now, wait for a segment
|
||||||
|
if(ite == ts_objects.end()){
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((target_duration > 0)? target_duration:DEFAULT_TS_DURATION);
|
||||||
|
Trace("[TS] no fresh ts, wait for a while. sleep %dms", sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AggregateRet aggregate_ret;
|
||||||
|
|
||||||
|
// to process from the specified ite
|
||||||
|
for(; ite != ts_objects.end(); ++ite){
|
||||||
|
M3u8TS ts_object = *ite;
|
||||||
|
|
||||||
|
if(!is_vod){
|
||||||
|
last_downloaded_ts = ts_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("start to process ts %s", ts_object.ts_url.c_str());
|
||||||
|
|
||||||
|
aggregate_ret.Add(DownloadTS(client, ts_object));
|
||||||
|
}
|
||||||
|
|
||||||
|
return aggregate_ret.GetReturnValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHlsTask::DownloadTS(StHttpClient& client, M3u8TS& ts){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
HttpUrl url;
|
||||||
|
|
||||||
|
if((ret = url.Initialize(ts.ts_url)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize ts url failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("[TS] url=%s, duration=%.2f, delay=%.2f", url.GetUrl(), ts.duration, delay_seconds);
|
||||||
|
statistic->OnSubTaskStart(GetId(), ts.ts_url);
|
||||||
|
|
||||||
|
if((ret = client.DownloadString(&url, NULL)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnSubTaskError(GetId(), (int)ts.duration);
|
||||||
|
|
||||||
|
Error("http client download ts file %s failed. ret=%d", url.GetUrl(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:ts.duration);
|
||||||
|
Trace("[TS] url=%s download, duration=%.2f, delay=%.2f, size=%"PRId64", sleep %dms",
|
||||||
|
url.GetUrl(), ts.duration, delay_seconds, client.GetResponseHeader()->content_length, sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
statistic->OnSubTaskEnd(GetId(), (int)ts.duration);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_hls_load_hpp
|
||||||
|
#define _htl_app_hls_load_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_hls_load.hpp>
|
||||||
|
*/
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
#include <htl_app_m3u8_parser.hpp>
|
||||||
|
#include <htl_app_task_base.hpp>
|
||||||
|
|
||||||
|
// for http task.
|
||||||
|
class StHlsTask : public StBaseTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
HttpUrl url;
|
||||||
|
// the last downloaded ts url to prevent download multile times.
|
||||||
|
M3u8TS last_downloaded_ts;
|
||||||
|
int target_duration;
|
||||||
|
bool is_vod;
|
||||||
|
public:
|
||||||
|
StHlsTask();
|
||||||
|
virtual ~StHlsTask();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url, bool vod, double startup, double delay, double error, int count);
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri();
|
||||||
|
virtual int ProcessTask();
|
||||||
|
private:
|
||||||
|
virtual int ProcessM3u8(StHttpClient& client);
|
||||||
|
virtual int ProcessTS(StHttpClient& client, std::vector<M3u8TS>& ts_objects);
|
||||||
|
virtual int DownloadTS(StHttpClient& client, M3u8TS& ts);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
|
||||||
|
StHttpClient::StHttpClient(){
|
||||||
|
socket = new StSocket();
|
||||||
|
connected_url = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
StHttpClient::~StHttpClient(){
|
||||||
|
delete socket;
|
||||||
|
socket = NULL;
|
||||||
|
|
||||||
|
delete connected_url;
|
||||||
|
connected_url = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::DownloadString(HttpUrl* url, std::string* response){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = CheckUrl(url)) != ERROR_SUCCESS){
|
||||||
|
Error("http client check url failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = Connect(url)) != ERROR_SUCCESS){
|
||||||
|
Error("http client connect url failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send GET request to read content
|
||||||
|
// GET %s HTTP/1.1\r\nHost: %s\r\n\r\n
|
||||||
|
stringstream ss;
|
||||||
|
ss << "GET " << url->GetPath() << " "
|
||||||
|
<< "HTTP/1.1\r\n"
|
||||||
|
<< "Host: " << url->GetHost() << "\r\n"
|
||||||
|
<< "Connection: Keep-Alive" << "\r\n"
|
||||||
|
<< "User-Agent: " << ProductHTTPName << "\r\n"
|
||||||
|
<< "\r\n";
|
||||||
|
|
||||||
|
ssize_t nwrite;
|
||||||
|
if((ret = socket->Write(ss.str().c_str(), ss.str().length(), &nwrite)) != ERROR_SUCCESS){
|
||||||
|
Error("write to server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = ParseResponse(url, response)) != ERROR_SUCCESS){
|
||||||
|
Error("http client parse response failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_parser* StHttpClient::GetResponseHeader(){
|
||||||
|
return &http_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::on_headers_complete(http_parser* parser){
|
||||||
|
StHttpClient* obj = (StHttpClient*)parser->data;
|
||||||
|
obj->OnHeaderCompleted(parser);
|
||||||
|
|
||||||
|
// see http_parser.c:1570, return 1 to skip body.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StHttpClient::OnHeaderCompleted(http_parser* parser){
|
||||||
|
// save the parser status when header parse completed.
|
||||||
|
memcpy(&http_header, parser, sizeof(http_header));
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::ParseResponse(HttpUrl* url, string* response){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
int body_received = 0;
|
||||||
|
if((ret = ParseResponseHeader(response, body_received)) != ERROR_SUCCESS){
|
||||||
|
Error("parse response header failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = ParseResponseBody(url, response, body_received)) != ERROR_SUCCESS){
|
||||||
|
Error("parse response body failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("url %s download, body size=%"PRId64, url->GetUrl(), http_header.content_length);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::ParseResponseBody(HttpUrl* url, string* response, int body_received){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(url != NULL);
|
||||||
|
|
||||||
|
uint64_t body_left = http_header.content_length - body_received;
|
||||||
|
|
||||||
|
if(response != NULL){
|
||||||
|
char buf[HTTP_BODY_BUFFER];
|
||||||
|
return ParseResponseBodyData(url, response, (size_t)body_left, (const void*)buf, (size_t)HTTP_BODY_BUFFER);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// if ignore response, use shared fast memory.
|
||||||
|
static char buf[HTTP_BODY_BUFFER];
|
||||||
|
return ParseResponseBodyData(url, response, (size_t)body_left, (const void*)buf, (size_t)HTTP_BODY_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::ParseResponseBodyData(HttpUrl* url, string* response, size_t body_left, const void* buf, size_t size){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(url != NULL);
|
||||||
|
|
||||||
|
while(body_left > 0){
|
||||||
|
ssize_t nread;
|
||||||
|
if((ret = socket->Read(buf, (size < body_left)? size:body_left, &nread)) != ERROR_SUCCESS){
|
||||||
|
Error("read header from server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(response != NULL && nread > 0){
|
||||||
|
response->append((char*)buf, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_left -= nread;
|
||||||
|
Info("read url(%s) content partial %"PRId64"/%"PRId64"",
|
||||||
|
url->GetUrl(), http_header.content_length - body_left, http_header.content_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::ParseResponseHeader(string* response, int& body_received){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
http_parser_settings settings;
|
||||||
|
|
||||||
|
memset(&settings, 0, sizeof(settings));
|
||||||
|
settings.on_headers_complete = on_headers_complete;
|
||||||
|
|
||||||
|
http_parser parser;
|
||||||
|
http_parser_init(&parser, HTTP_RESPONSE);
|
||||||
|
// callback object ptr.
|
||||||
|
parser.data = (void*)this;
|
||||||
|
|
||||||
|
// reset response header.
|
||||||
|
memset(&http_header, 0, sizeof(http_header));
|
||||||
|
|
||||||
|
// parser header.
|
||||||
|
char buf[HTTP_HEADER_BUFFER];
|
||||||
|
for(;;){
|
||||||
|
ssize_t nread;
|
||||||
|
if((ret = socket->Read((const void*)buf, (size_t)sizeof(buf), &nread)) != ERROR_SUCCESS){
|
||||||
|
Error("read body from server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nparsed = http_parser_execute(&parser, &settings, buf, nread);
|
||||||
|
Info("read_size=%d, nparsed=%d", (int)nread, (int)nparsed);
|
||||||
|
|
||||||
|
// check header size.
|
||||||
|
if(http_header.nread != 0){
|
||||||
|
body_received = nread - nparsed;
|
||||||
|
|
||||||
|
Info("http header parsed, size=%d, content-length=%"PRId64", body-received=%d",
|
||||||
|
http_header.nread, http_header.content_length, body_received);
|
||||||
|
|
||||||
|
if(response != NULL && body_received > 0){
|
||||||
|
response->append(buf + nparsed, body_received);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nparsed != nread){
|
||||||
|
ret = ERROR_HP_PARSE_RESPONSE;
|
||||||
|
Error("parse response error, parsed(%d)!=read(%d), ret=%d", (int)nparsed, (int)nread, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::Connect(HttpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(socket->Status() == SocketConnected){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ip;
|
||||||
|
if((ret = StUtility::DnsResolve(url->GetHost(), ip)) != ERROR_SUCCESS){
|
||||||
|
Error("dns resolve failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = socket->Connect(ip.c_str(), url->GetPort())) != ERROR_SUCCESS){
|
||||||
|
Error("connect to server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("socket connected on url %s", url->GetUrl());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpClient::CheckUrl(HttpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(connected_url == NULL){
|
||||||
|
connected_url = url->Copy();
|
||||||
|
|
||||||
|
if((ret = StUtility::DnsResolve(connected_url->GetHost(), connected_ip)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string ip;
|
||||||
|
if((ret = StUtility::DnsResolve(url->GetHost(), ip)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ip != connected_ip || connected_url->GetPort() != url->GetPort()){
|
||||||
|
ret = ERROR_HP_EP_CHNAGED;
|
||||||
|
Error("invalid url=%s, endpoint change from %s:%d to %s:%d",
|
||||||
|
url->GetUrl(), connected_ip.c_str(), connected_url->GetPort(), ip.c_str(), url->GetPort());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_http_client_hpp
|
||||||
|
#define _htl_app_http_client_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
|
||||||
|
class StHttpClient
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string connected_ip;
|
||||||
|
HttpUrl* connected_url;
|
||||||
|
StSocket* socket;
|
||||||
|
private:
|
||||||
|
http_parser http_header;
|
||||||
|
public:
|
||||||
|
StHttpClient();
|
||||||
|
virtual ~StHttpClient();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* download the content specified by url using HTTP GET method.
|
||||||
|
* @response the string pointer which store the content. ignore content if set to NULL.
|
||||||
|
*/
|
||||||
|
virtual int DownloadString(HttpUrl* url, std::string* response);
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* when get response and parse header completed, return the header.
|
||||||
|
* if not parsed, header set to zero.
|
||||||
|
*/
|
||||||
|
virtual http_parser* GetResponseHeader();
|
||||||
|
private:
|
||||||
|
static int on_headers_complete(http_parser* parser);
|
||||||
|
virtual void OnHeaderCompleted(http_parser* parser);
|
||||||
|
private:
|
||||||
|
virtual int ParseResponse(HttpUrl* url, string* response);
|
||||||
|
virtual int ParseResponseBody(HttpUrl* url, string* response, int body_received);
|
||||||
|
virtual int ParseResponseBodyData(HttpUrl* url, string* response, size_t body_left, const void* buf, size_t size);
|
||||||
|
virtual int ParseResponseHeader(string* response, int& body_received);
|
||||||
|
virtual int Connect(HttpUrl* url);
|
||||||
|
virtual int CheckUrl(HttpUrl* url);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_http_load.hpp>
|
||||||
|
|
||||||
|
StHttpTask::StHttpTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StHttpTask::~StHttpTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpTask::Initialize(std::string http_url, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri* StHttpTask::GetUri(){
|
||||||
|
return &url;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StHttpTask::ProcessTask(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Trace("start to process HTTP task #%d, schema=%s, host=%s, port=%d, path=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d",
|
||||||
|
GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetPath(), startup_seconds, delay_seconds, error_seconds, count);
|
||||||
|
|
||||||
|
StHttpClient client;
|
||||||
|
|
||||||
|
// if count is zero, infinity loop.
|
||||||
|
for(int i = 0; count == 0 || i < count; i++){
|
||||||
|
statistic->OnTaskStart(GetId(), url.GetUrl());
|
||||||
|
|
||||||
|
if((ret = client.DownloadString(&url, NULL)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnTaskError(GetId(), 0);
|
||||||
|
|
||||||
|
Error("http client get url failed. ret=%d", ret);
|
||||||
|
st_usleep((st_utime_t)(error_seconds * 1000 * 1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0);
|
||||||
|
Info("[HTTP] %s download, size=%"PRId64", sleep %dms", url.GetUrl(), client.GetResponseHeader()->content_length, sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
statistic->OnTaskEnd(GetId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_http_load_hpp
|
||||||
|
#define _htl_app_http_load_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_http_load.hpp>
|
||||||
|
*/
|
||||||
|
#include <htl_app_task_base.hpp>
|
||||||
|
|
||||||
|
// for http task.
|
||||||
|
class StHttpTask : public StBaseTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
HttpUrl url;
|
||||||
|
public:
|
||||||
|
StHttpTask();
|
||||||
|
virtual ~StHttpTask();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url, double startup, double delay, double error, int count);
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri();
|
||||||
|
virtual int ProcessTask();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_m3u8_parser.hpp>
|
||||||
|
|
||||||
|
class String
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
string value;
|
||||||
|
public:
|
||||||
|
String(string str = ""){
|
||||||
|
value = str;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
String& set_str(string str){
|
||||||
|
value = str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
int length(){
|
||||||
|
return (int)value.length();
|
||||||
|
}
|
||||||
|
bool startswith(string key, String* left = NULL){
|
||||||
|
size_t pos = value.find(key);
|
||||||
|
|
||||||
|
if(pos == 0 && left != NULL){
|
||||||
|
left->set_str(value.substr(pos + key.length()));
|
||||||
|
left->strip();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos == 0;
|
||||||
|
}
|
||||||
|
bool endswith(string key, String* left = NULL){
|
||||||
|
size_t pos = value.rfind(key);
|
||||||
|
|
||||||
|
if(pos == value.length() - key.length() && left != NULL){
|
||||||
|
left->set_str(value.substr(pos));
|
||||||
|
left->strip();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos == value.length() - key.length();
|
||||||
|
}
|
||||||
|
String& strip(){
|
||||||
|
if (value.empty()) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro to test whether char is a space.
|
||||||
|
#define __IS_SPACE(ch) (ch == '\n' || ch == '\r' || ch == ' ')
|
||||||
|
|
||||||
|
// find the start and end which is not space.
|
||||||
|
char* bytes = (char*)value.data();
|
||||||
|
|
||||||
|
// find the end not space
|
||||||
|
char* end = NULL;
|
||||||
|
for (end = bytes + value.length() - 1; end > bytes && __IS_SPACE(end[0]); end--) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the start not space
|
||||||
|
char* start = NULL;
|
||||||
|
for (start = bytes; start < end && __IS_SPACE(start[0]); start++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the data to trim.
|
||||||
|
value = value.substr(start - bytes, end - start + 1);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
string getline(){
|
||||||
|
size_t pos = string::npos;
|
||||||
|
|
||||||
|
if((pos = value.find("\n")) != string::npos){
|
||||||
|
return value.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
String& remove(int size){
|
||||||
|
if(size >= (int)value.length()){
|
||||||
|
value = "";
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.substr(size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
const char* c_str(){
|
||||||
|
return value.c_str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HlsM3u8Parser::HlsM3u8Parser(){
|
||||||
|
}
|
||||||
|
|
||||||
|
HlsM3u8Parser::~HlsM3u8Parser(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int HlsM3u8Parser::ParseM3u8Data(HttpUrl* url, string m3u8, vector<M3u8TS>& ts_objects, int& target_duration){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
String data(m3u8);
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.3.1
|
||||||
|
// An Extended M3U file is distinguished from a basic M3U file by its
|
||||||
|
// first line, which MUST be the tag #EXTM3U.
|
||||||
|
if(!data.startswith("#EXTM3U")){
|
||||||
|
ret = ERROR_HLS_INVALID;
|
||||||
|
Error("invalid hls, #EXTM3U not found. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String value;
|
||||||
|
|
||||||
|
M3u8TS ts_object;
|
||||||
|
while(data.length() > 0){
|
||||||
|
String line;
|
||||||
|
data.remove(line.set_str(data.strip().getline()).strip().length()).strip();
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.2
|
||||||
|
// #EXT-X-TARGETDURATION:<s>
|
||||||
|
// where s is an integer indicating the target duration in seconds.
|
||||||
|
if(line.startswith("#EXT-X-TARGETDURATION:", &value)){
|
||||||
|
target_duration = atoi(value.c_str());
|
||||||
|
ts_object.duration = target_duration;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.3.2
|
||||||
|
// #EXTINF:<duration>,<title>
|
||||||
|
// "duration" is an integer or floating-point number in decimal
|
||||||
|
// positional notation that specifies the duration of the media segment
|
||||||
|
// in seconds. Durations that are reported as integers SHOULD be
|
||||||
|
// rounded to the nearest integer. Durations MUST be integers if the
|
||||||
|
// protocol version of the Playlist file is less than 3. The remainder
|
||||||
|
// of the line following the comma is an optional human-readable
|
||||||
|
// informative title of the media segment.
|
||||||
|
// ignore others util EXTINF
|
||||||
|
if(line.startswith("#EXTINF:", &value)){
|
||||||
|
ts_object.duration = atof(value.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!line.startswith("#")){
|
||||||
|
ts_object.ts_url = url->Resolve(line.c_str());
|
||||||
|
ts_objects.push_back(ts_object);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_m3u8_parser_hpp
|
||||||
|
#define _htl_app_m3u8_parser_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_m3u8_parser.hpp>
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
|
||||||
|
struct M3u8TS
|
||||||
|
{
|
||||||
|
std::string ts_url;
|
||||||
|
double duration;
|
||||||
|
|
||||||
|
bool operator== (const M3u8TS& b)const{
|
||||||
|
return ts_url == b.ts_url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HlsM3u8Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HlsM3u8Parser();
|
||||||
|
virtual ~HlsM3u8Parser();
|
||||||
|
public:
|
||||||
|
static int ParseM3u8Data(HttpUrl* url, std::string m3u8, std::vector<M3u8TS>& ts_objects, int& target_duration);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_app_rtmp_play.hpp>
|
||||||
|
#include <htl_app_rtmp_publish.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_load.hpp>
|
||||||
|
|
||||||
|
StRtmpTask::StRtmpTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpTask::~StRtmpTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpTask::Initialize(string http_url, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri* StRtmpTask::GetUri(){
|
||||||
|
return &url;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpTask::ProcessTask(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Trace("start to process RTMP play task #%d, schema=%s, host=%s, port=%d, tcUrl=%s, stream=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d",
|
||||||
|
GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetTcUrl(), url.GetStream(), startup_seconds, delay_seconds, error_seconds, count);
|
||||||
|
|
||||||
|
StRtmpPlayClient client;
|
||||||
|
|
||||||
|
// if count is zero, infinity loop.
|
||||||
|
for(int i = 0; count == 0 || i < count; i++){
|
||||||
|
statistic->OnTaskStart(GetId(), url.GetUrl());
|
||||||
|
|
||||||
|
if((ret = client.Dump(&url)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnTaskError(GetId(), 0);
|
||||||
|
|
||||||
|
Error("rtmp client dump url failed. ret=%d", ret);
|
||||||
|
st_usleep((st_utime_t)(error_seconds * 1000 * 1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0);
|
||||||
|
Trace("[RTMP] %s dump success, sleep %dms", url.GetUrl(), sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
statistic->OnTaskEnd(GetId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpTaskFast::StRtmpTaskFast(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpTaskFast::~StRtmpTaskFast(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpTaskFast::Initialize(string http_url, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri* StRtmpTaskFast::GetUri(){
|
||||||
|
return &url;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpTaskFast::ProcessTask(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Trace("start to process RTMP play fast task #%d, schema=%s, host=%s, port=%d, tcUrl=%s, stream=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d",
|
||||||
|
GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetTcUrl(), url.GetStream(), startup_seconds, delay_seconds, error_seconds, count);
|
||||||
|
|
||||||
|
StRtmpPlayClientFast client;
|
||||||
|
|
||||||
|
// if count is zero, infinity loop.
|
||||||
|
for(int i = 0; count == 0 || i < count; i++){
|
||||||
|
statistic->OnTaskStart(GetId(), url.GetUrl());
|
||||||
|
|
||||||
|
if((ret = client.Dump(&url)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnTaskError(GetId(), 0);
|
||||||
|
|
||||||
|
Error("rtmp client dump url failed. ret=%d", ret);
|
||||||
|
st_usleep((st_utime_t)(error_seconds * 1000 * 1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0);
|
||||||
|
Trace("[RTMP] %s dump success, sleep %dms", url.GetUrl(), sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
statistic->OnTaskEnd(GetId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPublishTask::StRtmpPublishTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPublishTask::~StRtmpPublishTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishTask::Initialize(string input, string http_url, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
input_flv_file = input;
|
||||||
|
|
||||||
|
if((ret = InitializeBase(http_url, startup, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri* StRtmpPublishTask::GetUri(){
|
||||||
|
return &url;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishTask::ProcessTask(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Trace("start to process RTMP publish task #%d, schema=%s, host=%s, port=%d, tcUrl=%s, stream=%s, startup=%.2f, delay=%.2f, error=%.2f, count=%d",
|
||||||
|
GetId(), url.GetSchema(), url.GetHost(), url.GetPort(), url.GetTcUrl(), url.GetStream(), startup_seconds, delay_seconds, error_seconds, count);
|
||||||
|
|
||||||
|
StRtmpPublishClient client;
|
||||||
|
|
||||||
|
// if count is zero, infinity loop.
|
||||||
|
for(int i = 0; count == 0 || i < count; i++){
|
||||||
|
statistic->OnTaskStart(GetId(), url.GetUrl());
|
||||||
|
|
||||||
|
if((ret = client.Publish(input_flv_file, &url)) != ERROR_SUCCESS){
|
||||||
|
statistic->OnTaskError(GetId(), 0);
|
||||||
|
|
||||||
|
Error("rtmp client publish url failed. ret=%d", ret);
|
||||||
|
st_usleep((st_utime_t)(error_seconds * 1000 * 1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime((delay_seconds >= 0)? delay_seconds:0);
|
||||||
|
Trace("[RTMP] %s publish success, sleep %dms", url.GetUrl(), sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
|
||||||
|
statistic->OnTaskEnd(GetId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_rtmp_load_hpp
|
||||||
|
#define _htl_app_rtmp_load_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_rtmp_load.hpp>
|
||||||
|
*/
|
||||||
|
#include <htl_app_task_base.hpp>
|
||||||
|
|
||||||
|
// for rtmp task.
|
||||||
|
class StRtmpTask : public StBaseTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
RtmpUrl url;
|
||||||
|
public:
|
||||||
|
StRtmpTask();
|
||||||
|
virtual ~StRtmpTask();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url, double startup, double delay, double error, int count);
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri();
|
||||||
|
virtual int ProcessTask();
|
||||||
|
};
|
||||||
|
|
||||||
|
// for rtmp task with fast algorihtm.
|
||||||
|
// donot recv in RTMP, but directly in TCP.
|
||||||
|
class StRtmpTaskFast : public StBaseTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
RtmpUrl url;
|
||||||
|
public:
|
||||||
|
StRtmpTaskFast();
|
||||||
|
virtual ~StRtmpTaskFast();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url, double startup, double delay, double error, int count);
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri();
|
||||||
|
virtual int ProcessTask();
|
||||||
|
};
|
||||||
|
|
||||||
|
// for rtmp publish load task.
|
||||||
|
class StRtmpPublishTask : public StBaseTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string input_flv_file;
|
||||||
|
RtmpUrl url;
|
||||||
|
public:
|
||||||
|
StRtmpPublishTask();
|
||||||
|
virtual ~StRtmpPublishTask();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string input,
|
||||||
|
std::string http_url, double startup, double delay, double error, int count);
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri();
|
||||||
|
virtual int ProcessTask();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_play.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
#include <htl_app_srs_hijack.hpp>
|
||||||
|
|
||||||
|
#define SOCK_READ_BUFFER 4096
|
||||||
|
#define SOCK_READV_NB 1024
|
||||||
|
#define SOCK_MERGED_READ_MS 800
|
||||||
|
|
||||||
|
StRtmpPlayClient::StRtmpPlayClient(){
|
||||||
|
stream_id = 0;
|
||||||
|
srs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPlayClient::~StRtmpPlayClient(){
|
||||||
|
srs_rtmp_destroy(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::Dump(RtmpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = Connect(url)) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client connect server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = Handshake()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client handshake failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client handshake success");
|
||||||
|
|
||||||
|
if((ret = ConnectApp()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client connect tcUrl failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client connect tcUrl(%s) success", url->GetTcUrl());
|
||||||
|
|
||||||
|
if((ret = PlayStram()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client play stream failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client play stream(%s) success", url->GetUrl());
|
||||||
|
|
||||||
|
if((ret = DumpAV()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client dump av failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client dump av success");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::Connect(RtmpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
srs_rtmp_destroy(srs);
|
||||||
|
srs = srs_rtmp_create(url->GetUrl());
|
||||||
|
|
||||||
|
if ((ret = srs_rtmp_dns_resolve(srs)) != ERROR_SUCCESS){
|
||||||
|
Error("dns resolve failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = srs_rtmp_connect_server(srs)) != ERROR_SUCCESS){
|
||||||
|
Error("connect to server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("socket connected on url %s", url->GetUrl());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::Handshake(){
|
||||||
|
return srs_rtmp_do_simple_handshake(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::ConnectApp(){
|
||||||
|
return srs_rtmp_connect_app(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::PlayStram(){
|
||||||
|
return srs_rtmp_play_stream(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClient::DumpAV(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// recv response
|
||||||
|
while(true){
|
||||||
|
char type;
|
||||||
|
u_int32_t timestamp;
|
||||||
|
char* data;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if ((ret = srs_rtmp_read_packet(srs, &type, ×tamp, &data, &size)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("get message type=%d, size=%d", type, size);
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPlayClientFast::StRtmpPlayClientFast(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPlayClientFast::~StRtmpPlayClientFast(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPlayClientFast::DumpAV(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
StSocket* skt = srs_hijack_get(srs);
|
||||||
|
|
||||||
|
// use buffered iovs to read tcp data.
|
||||||
|
char buf[SOCK_READ_BUFFER];
|
||||||
|
iovec iovs[SOCK_READV_NB];
|
||||||
|
|
||||||
|
for (int i = 0; i < SOCK_READV_NB; i++) {
|
||||||
|
iovec& iov = iovs[i];
|
||||||
|
iov.iov_base = buf;
|
||||||
|
iov.iov_len = SOCK_READ_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recv response
|
||||||
|
while(true){
|
||||||
|
ssize_t size = 0;
|
||||||
|
if ((ret = skt->Readv(iovs, SOCK_READV_NB, &size)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("get message size=%d", size);
|
||||||
|
|
||||||
|
// merged read.
|
||||||
|
st_usleep(SOCK_MERGED_READ_MS * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_rtmp_play_hpp
|
||||||
|
#define _htl_app_rtmp_play_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_rtmp_play.hpp>
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
class StRtmpPlayClient
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
srs_rtmp_t srs;
|
||||||
|
int stream_id;
|
||||||
|
public:
|
||||||
|
StRtmpPlayClient();
|
||||||
|
virtual ~StRtmpPlayClient();
|
||||||
|
public:
|
||||||
|
virtual int Dump(RtmpUrl* url);
|
||||||
|
protected:
|
||||||
|
virtual int Connect(RtmpUrl* url);
|
||||||
|
virtual int Handshake();
|
||||||
|
virtual int ConnectApp();
|
||||||
|
virtual int PlayStram();
|
||||||
|
virtual int DumpAV();
|
||||||
|
};
|
||||||
|
|
||||||
|
class StRtmpPlayClientFast : public StRtmpPlayClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StRtmpPlayClientFast();
|
||||||
|
virtual ~StRtmpPlayClientFast();
|
||||||
|
protected:
|
||||||
|
virtual int DumpAV();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_publish.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
StRtmpPublishClient::StRtmpPublishClient(){
|
||||||
|
stream_id = 0;
|
||||||
|
srs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
StRtmpPublishClient::~StRtmpPublishClient(){
|
||||||
|
srs_rtmp_destroy(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::Publish(string input, RtmpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = Connect(url)) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client connect server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = Handshake()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client handshake failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client handshake success");
|
||||||
|
|
||||||
|
if((ret = ConnectApp()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client connect tcUrl failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client connect tcUrl(%s) success", url->GetTcUrl());
|
||||||
|
|
||||||
|
if((ret = PublishStram()) != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client publish stream failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client publish stream(%s) success", url->GetUrl());
|
||||||
|
|
||||||
|
srs_flv_t flv = srs_flv_open_read(input.c_str());
|
||||||
|
if (!flv) {
|
||||||
|
ret = ERROR_RTMP_OPEN_FLV;
|
||||||
|
Error("open flv file %s failed. ret=%d", input.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t timebase = 0;
|
||||||
|
while (true) {
|
||||||
|
int32_t starttime = -1;
|
||||||
|
int32_t endtime = -1;
|
||||||
|
|
||||||
|
// publish the whole av of flv file
|
||||||
|
ret = PublishAV(flv, timebase, &starttime, &endtime);
|
||||||
|
|
||||||
|
// restart when flv EOF
|
||||||
|
if (srs_flv_is_eof(ret)) {
|
||||||
|
srs_flv_lseek(flv, 0);
|
||||||
|
timebase += endtime - starttime;
|
||||||
|
Info("republish for flv EOF, timebase=%"PRId64", start=%d, end=%d, ret=%d",
|
||||||
|
timebase, starttime, endtime, ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srs_flv_close(flv);
|
||||||
|
|
||||||
|
if(ret != ERROR_SUCCESS){
|
||||||
|
Error("rtmp client dump av failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("rtmp client dump av success");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::Connect(RtmpUrl* url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
srs_rtmp_destroy(srs);
|
||||||
|
srs = srs_rtmp_create(url->GetUrl());
|
||||||
|
|
||||||
|
if ((ret = srs_rtmp_dns_resolve(srs)) != ERROR_SUCCESS){
|
||||||
|
Error("dns resolve failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = srs_rtmp_connect_server(srs)) != ERROR_SUCCESS){
|
||||||
|
Error("connect to server failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("socket connected on url %s", url->GetUrl());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::Handshake(){
|
||||||
|
return srs_rtmp_do_simple_handshake(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::ConnectApp(){
|
||||||
|
return srs_rtmp_connect_app(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::PublishStram(){
|
||||||
|
return srs_rtmp_publish_stream(srs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StRtmpPublishClient::PublishAV(srs_flv_t flv,
|
||||||
|
int64_t timebase, int32_t* starttime, int32_t* endtime
|
||||||
|
){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
char header[9];
|
||||||
|
if ((ret = srs_flv_read_header(flv, header)) != ERROR_SUCCESS) {
|
||||||
|
Error("read flv header failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open flv and publish to server.
|
||||||
|
u_int32_t re = 0;
|
||||||
|
while(true){
|
||||||
|
char type;
|
||||||
|
u_int32_t timestamp;
|
||||||
|
int32_t size;
|
||||||
|
|
||||||
|
if ((ret = srs_flv_read_tag_header(flv, &type, &size, ×tamp)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the start and end time.
|
||||||
|
if (*starttime < 0) {
|
||||||
|
*starttime = timestamp;
|
||||||
|
}
|
||||||
|
*endtime = timestamp;
|
||||||
|
|
||||||
|
char* data = new char[size];
|
||||||
|
if ((ret = srs_flv_read_tag_data(flv, data, size)) != ERROR_SUCCESS) {
|
||||||
|
delete data;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// modify the duration to -1
|
||||||
|
if (type == SRS_RTMP_TYPE_SCRIPT) {
|
||||||
|
int nparsed = 0;
|
||||||
|
srs_amf0_t onMetaData = srs_amf0_parse(data, size, &nparsed);
|
||||||
|
srs_amf0_t metadata = srs_amf0_parse(data + nparsed, size - nparsed, &nparsed);
|
||||||
|
if (srs_amf0_is_ecma_array(metadata)) {
|
||||||
|
srs_amf0_t obj = srs_amf0_ecma_array_to_object(metadata);
|
||||||
|
srs_amf0_free(metadata);
|
||||||
|
metadata = obj;
|
||||||
|
}
|
||||||
|
if (srs_amf0_object_property(metadata, "duration")) {
|
||||||
|
srs_amf0_object_property_set(metadata, "duration", srs_amf0_create_number(-1));
|
||||||
|
|
||||||
|
// serialize to bytes.
|
||||||
|
int nb_onMetaData = srs_amf0_size(onMetaData);
|
||||||
|
int nb_metadata = srs_amf0_size(metadata);
|
||||||
|
size = nb_onMetaData + nb_metadata;
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
data = new char[size];
|
||||||
|
|
||||||
|
ret = srs_amf0_serialize(onMetaData, data, nb_onMetaData);
|
||||||
|
if (ret == ERROR_SUCCESS) {
|
||||||
|
ret = srs_amf0_serialize(metadata, data + nb_onMetaData, nb_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_amf0_free(onMetaData);
|
||||||
|
srs_amf0_free(metadata);
|
||||||
|
|
||||||
|
if (ret != ERROR_SUCCESS) {
|
||||||
|
delete data;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int32_t dts = (u_int32_t)(timebase + timestamp);
|
||||||
|
if ((ret = srs_rtmp_write_packet(srs, type, dts, data, size)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("send message type=%d, size=%d, time=%d, dts=%d",
|
||||||
|
type, size, timestamp, dts);
|
||||||
|
|
||||||
|
if (re <= 0) {
|
||||||
|
re = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timestamp - re > 300) {
|
||||||
|
st_usleep((timestamp - re) * 1000);
|
||||||
|
re = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_rtmp_publish_hpp
|
||||||
|
#define _htl_app_rtmp_publish_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_rtmp_publish.hpp>
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
class StRtmpPublishClient
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
srs_rtmp_t srs;
|
||||||
|
int stream_id;
|
||||||
|
public:
|
||||||
|
StRtmpPublishClient();
|
||||||
|
virtual ~StRtmpPublishClient();
|
||||||
|
public:
|
||||||
|
virtual int Publish(std::string input, RtmpUrl* url);
|
||||||
|
private:
|
||||||
|
virtual int Connect(RtmpUrl* url);
|
||||||
|
virtual int Handshake();
|
||||||
|
virtual int ConnectApp();
|
||||||
|
virtual int PublishStram();
|
||||||
|
virtual int PublishAV(srs_flv_t flv,
|
||||||
|
int64_t timebase, int32_t* starttime, int32_t* endtime
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
#include <htl_app_srs_hijack.hpp>
|
||||||
|
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
#ifndef SRS_HIJACK_IO
|
||||||
|
#error "must hijack the srs-librtmp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
StSocket* srs_hijack_get(srs_rtmp_t rtmp)
|
||||||
|
{
|
||||||
|
srs_hijack_io_t ctx = srs_hijack_io_get(rtmp);
|
||||||
|
return (StSocket*)ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_hijack_io_t srs_hijack_io_create()
|
||||||
|
{
|
||||||
|
return new StSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_hijack_io_destroy(srs_hijack_io_t ctx)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
delete skt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_create_socket(srs_hijack_io_t /*ctx*/)
|
||||||
|
{
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
return skt->Connect(server_ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
return skt->Read(buf, size, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_hijack_io_set_recv_timeout(srs_hijack_io_t /*ctx*/, int64_t /*timeout_us*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t /*ctx*/)
|
||||||
|
{
|
||||||
|
return ST_UTIME_NO_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t /*ctx*/)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_hijack_io_set_send_timeout(srs_hijack_io_t /*ctx*/, int64_t /*timeout_us*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t /*ctx*/)
|
||||||
|
{
|
||||||
|
return ST_UTIME_NO_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t /*ctx*/)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
return skt->Writev(iov, iov_size, nwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool srs_hijack_io_is_never_timeout(srs_hijack_io_t /*ctx*/, int64_t /*timeout_us*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
return skt->ReadFully(buf, size, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
StSocket* skt = (StSocket*)ctx;
|
||||||
|
return skt->Write(buf, size, nwrite);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_srs_hijack_hpp
|
||||||
|
#define _htl_app_srs_hijack_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_srs_hijack.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
class StSocket;
|
||||||
|
extern StSocket* srs_hijack_get(srs_rtmp_t rtmp);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_app_http_client.hpp>
|
||||||
|
|
||||||
|
#include <htl_app_task_base.hpp>
|
||||||
|
|
||||||
|
StBaseTask::StBaseTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StBaseTask::~StBaseTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StBaseTask::InitializeBase(std::string http_url, double startup, double delay, double error, int count){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = GetUri()->Initialize(http_url)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info("task url(%s) parsed, startup=%.2f, delay=%.2f, error=%.2f, count=%d", http_url.c_str(), startup, delay, error, count);
|
||||||
|
|
||||||
|
this->delay_seconds = delay;
|
||||||
|
this->startup_seconds = startup;
|
||||||
|
this->error_seconds = error;
|
||||||
|
this->count = count;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StBaseTask::Process(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(startup_seconds > 0){
|
||||||
|
int sleep_ms = StUtility::BuildRandomMTime(startup_seconds);
|
||||||
|
Trace("start random sleep %dms", sleep_ms);
|
||||||
|
st_usleep(sleep_ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = ProcessTask()) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_app_task_base_hpp
|
||||||
|
#define _htl_app_task_base_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_app_task_base.hpp>
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <http_parser.h>
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
|
||||||
|
class StBaseTask : public StTask
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
double startup_seconds;
|
||||||
|
double delay_seconds;
|
||||||
|
double error_seconds;
|
||||||
|
int count;
|
||||||
|
public:
|
||||||
|
StBaseTask();
|
||||||
|
virtual ~StBaseTask();
|
||||||
|
protected:
|
||||||
|
virtual int InitializeBase(std::string http_url, double startup, double delay, double error, int count);
|
||||||
|
public:
|
||||||
|
virtual int Process();
|
||||||
|
protected:
|
||||||
|
virtual Uri* GetUri() = 0;
|
||||||
|
virtual int ProcessTask() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
|
||||||
|
#include <htl_core_aggregate_ret.hpp>
|
||||||
|
|
||||||
|
AggregateRet::AggregateRet(){
|
||||||
|
}
|
||||||
|
|
||||||
|
AggregateRet::~AggregateRet(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void AggregateRet::Add(int ret){
|
||||||
|
rets.push_back(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AggregateRet::GetReturnValue(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
for(vector<int>::iterator ite = rets.begin(); ite != rets.end(); ++ite){
|
||||||
|
int item_ret = *ite;
|
||||||
|
|
||||||
|
if((ret = item_ret) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_core_aggregate_ret_hpp
|
||||||
|
#define _htl_core_aggregate_ret_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_core_aggregate_ret.hpp>
|
||||||
|
*/
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class AggregateRet
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<int> rets;
|
||||||
|
public:
|
||||||
|
AggregateRet();
|
||||||
|
virtual ~AggregateRet();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* add return value to collection.
|
||||||
|
*/
|
||||||
|
virtual void Add(int ret);
|
||||||
|
/**
|
||||||
|
* get the summary of return value, if any failed, return it.
|
||||||
|
*/
|
||||||
|
virtual int GetReturnValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_core_error_hpp
|
||||||
|
#define _htl_core_error_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ERROR_SUCCESS 0
|
||||||
|
|
||||||
|
#define ERROR_SOCKET 100
|
||||||
|
#define ERROR_OPEN_SOCKET 101
|
||||||
|
#define ERROR_CONNECT 102
|
||||||
|
#define ERROR_SEND 103
|
||||||
|
#define ERROR_READ 104
|
||||||
|
#define ERROR_CLOSE 105
|
||||||
|
#define ERROR_DNS_RESOLVE 106
|
||||||
|
|
||||||
|
#define ERROR_URL_INVALID 200
|
||||||
|
#define ERROR_HTTP_RESPONSE 201
|
||||||
|
#define ERROR_HLS_INVALID 202
|
||||||
|
|
||||||
|
#define ERROR_NOT_SUPPORT 300
|
||||||
|
|
||||||
|
#define ERROR_ST_INITIALIZE 400
|
||||||
|
#define ERROR_ST_THREAD_CREATE 401
|
||||||
|
|
||||||
|
#define ERROR_HP_PARSE_URL 500
|
||||||
|
#define ERROR_HP_EP_CHNAGED 501
|
||||||
|
#define ERROR_HP_PARSE_RESPONSE 502
|
||||||
|
|
||||||
|
#define ERROR_RTMP_URL 600
|
||||||
|
#define ERROR_RTMP_OVERFLOW 601
|
||||||
|
#define ERROR_RTMP_MSG_TOO_BIG 602
|
||||||
|
#define ERROR_RTMP_INVALID_RESPONSE 603
|
||||||
|
#define ERROR_RTMP_OPEN_FLV 604
|
||||||
|
|
||||||
|
#define ProductVersion "1.0.14"
|
||||||
|
#define ProductHTTPName "SB(SRS Bench) HttpLoad/"ProductVersion
|
||||||
|
#define ProductHLSName "SB(SRS Bench) HlsLoad/"ProductVersion
|
||||||
|
#define ProductRtmpName "SB(SRS Bench) RtmpPlayLoad/"ProductVersion
|
||||||
|
#define ProductRtmpPublishName "SB(SRS Bench) RtmpPublishLoad/"ProductVersion
|
||||||
|
#define BuildPlatform "linux"
|
||||||
|
#define BugReportEmail "winlin@vip.126.com"
|
||||||
|
|
||||||
|
#define HTTP_HEADER_BUFFER 1024
|
||||||
|
#define HTTP_BODY_BUFFER 32*1024
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
DateTime::DateTime(){
|
||||||
|
memset(time_data, 0, DATE_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime::~DateTime(){
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DateTime::FormatTime(){
|
||||||
|
// clock time
|
||||||
|
timeval tv;
|
||||||
|
if(gettimeofday(&tv, NULL) == -1){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// to calendar time
|
||||||
|
struct tm* tm;
|
||||||
|
if((tm = localtime(&tv.tv_sec)) == NULL){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// log header, the time/pid/level of log
|
||||||
|
// reserved 1bytes for the new line.
|
||||||
|
snprintf(time_data, DATE_LEN, "%d-%02d-%02d %02d:%02d:%02d.%03d",
|
||||||
|
1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
|
||||||
|
(int)(tv.tv_usec / 1000));
|
||||||
|
|
||||||
|
return time_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogContext::LogContext(){
|
||||||
|
}
|
||||||
|
|
||||||
|
LogContext::~LogContext(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int LogContext::GetId(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* LogContext::FormatTime(){
|
||||||
|
return time.FormatTime();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_core_log_hpp
|
||||||
|
#define _htl_core_log_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class DateTime
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// %d-%02d-%02d %02d:%02d:%02d.%03d
|
||||||
|
#define DATE_LEN 24
|
||||||
|
char time_data[DATE_LEN];
|
||||||
|
public:
|
||||||
|
DateTime();
|
||||||
|
virtual ~DateTime();
|
||||||
|
public:
|
||||||
|
virtual const char* FormatTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
class LogContext
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DateTime time;
|
||||||
|
public:
|
||||||
|
LogContext();
|
||||||
|
virtual ~LogContext();
|
||||||
|
public:
|
||||||
|
virtual void SetId(int id) = 0;
|
||||||
|
virtual int GetId();
|
||||||
|
public:
|
||||||
|
virtual const char* FormatTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
// user must implements the LogContext and define a global instance.
|
||||||
|
extern LogContext* context;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
#define Verbose(msg, ...) printf("[%s][%d][verbs] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#define Info(msg, ...) printf("[%s][%d][infos] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#define Trace(msg, ...) printf("[%s][%d][trace] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#define Warn(msg, ...) printf("[%s][%d][warns] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n")
|
||||||
|
#define Error(msg, ...) printf("[%s][%d][error] ", context->FormatTime(), context->GetId());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n")
|
||||||
|
#else
|
||||||
|
#define Verbose(msg, ...) printf("[%s][%d][verbs][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#define Info(msg, ...) printf("[%s][%d][infos][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#define Trace(msg, ...) printf("[%s][%d][trace][%s] ", context->FormatTime(), context->GetId(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n")
|
||||||
|
#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")
|
||||||
|
#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")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
#undef Verbose
|
||||||
|
#define Verbose(msg, ...) (void)0
|
||||||
|
#endif
|
||||||
|
#if 1
|
||||||
|
#undef Info
|
||||||
|
#define Info(msg, ...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// for summary/report thread, print to stderr.
|
||||||
|
#define LReport(msg, ...) fprintf(stderr, "[%s] ", context->FormatTime());fprintf(stderr, msg, ##__VA_ARGS__);fprintf(stderr, "\n")
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
|
||||||
|
Uri::Uri(){
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri::~Uri(){
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolUrl::ProtocolUrl(){
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolUrl::~ProtocolUrl(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProtocolUrl::Initialize(std::string http_url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
url = http_url;
|
||||||
|
const char* purl = url.c_str();
|
||||||
|
|
||||||
|
if((ret = http_parser_parse_url(purl, url.length(), 0, &hp_u)) != 0){
|
||||||
|
int code = ret;
|
||||||
|
ret = ERROR_HP_PARSE_URL;
|
||||||
|
|
||||||
|
Error("parse url %s failed, code=%d, ret=%d", purl, code, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Get(UF_SCHEMA) != ""){
|
||||||
|
schema = Get(UF_SCHEMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
host = Get(UF_HOST);
|
||||||
|
|
||||||
|
if(Get(UF_PORT) != ""){
|
||||||
|
port = atoi(Get(UF_PORT).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
path = Get(UF_PATH);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ProtocolUrl::GetUrl(){
|
||||||
|
return url.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ProtocolUrl::GetSchema(){
|
||||||
|
return schema.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ProtocolUrl::GetHost(){
|
||||||
|
return host.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProtocolUrl::GetPort(){
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtocolUrl::Get(http_parser_url_fields field){
|
||||||
|
return HttpUrl::GetUriField(url, &hp_u, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtocolUrl::GetUriField(string uri, http_parser_url* hp_u, http_parser_url_fields field){
|
||||||
|
if((hp_u->field_set & (1 << field)) == 0){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Verbose("uri field matched, off=%d, len=%d, value=%.*s",
|
||||||
|
hp_u->field_data[field].off, hp_u->field_data[field].len, hp_u->field_data[field].len,
|
||||||
|
uri.c_str() + hp_u->field_data[field].off);
|
||||||
|
|
||||||
|
return uri.substr(hp_u->field_data[field].off, hp_u->field_data[field].len);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpUrl::HttpUrl(){
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpUrl::~HttpUrl(){
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PROTOCOL_HTTP "http://"
|
||||||
|
#define PROTOCOL_HTTPS "https://"
|
||||||
|
string HttpUrl::Resolve(string origin_url){
|
||||||
|
string copy = origin_url;
|
||||||
|
|
||||||
|
size_t pos = string::npos;
|
||||||
|
string key = "./";
|
||||||
|
if((pos = origin_url.find(key)) == 0){
|
||||||
|
copy = origin_url.substr(key.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// uri
|
||||||
|
if(copy.find(PROTOCOL_HTTP) == 0 || copy.find(PROTOCOL_HTTPS) == 0){
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// abs or relative url
|
||||||
|
stringstream ss;
|
||||||
|
ss << schema << "://" << host;
|
||||||
|
|
||||||
|
if(port != 80){
|
||||||
|
ss << ":" << port;
|
||||||
|
}
|
||||||
|
|
||||||
|
// relative path
|
||||||
|
if(copy.find("/") != 0){
|
||||||
|
string dir = path;
|
||||||
|
|
||||||
|
if((pos = dir.rfind("/")) != string::npos){
|
||||||
|
dir = dir.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << dir << "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << copy;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpUrl* HttpUrl::Copy(){
|
||||||
|
HttpUrl* copy = new HttpUrl();
|
||||||
|
|
||||||
|
copy->Initialize(url);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* HttpUrl::GetPath(){
|
||||||
|
return path.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
RtmpUrl::RtmpUrl(){
|
||||||
|
port = 1935;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtmpUrl::~RtmpUrl(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int RtmpUrl::Initialize(std::string http_url){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if((ret = ProtocolUrl::Initialize(http_url)) != ERROR_SUCCESS){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support rewrite vhost in query.
|
||||||
|
vhost = host;
|
||||||
|
|
||||||
|
app = path.c_str() + 1;
|
||||||
|
|
||||||
|
size_t pos = string::npos;
|
||||||
|
if((pos = app.find("/")) == string::npos){
|
||||||
|
ret = ERROR_RTMP_URL;
|
||||||
|
Error("invalid rtmp url, no stream found, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = app.substr(pos + 1);
|
||||||
|
app = app.substr(0, pos);
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
ss << schema << "://" << vhost << ":" << port << "/" << app;
|
||||||
|
tcUrl = ss.str();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* RtmpUrl::GetTcUrl(){
|
||||||
|
return tcUrl.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* RtmpUrl::GetVhost(){
|
||||||
|
return vhost.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* RtmpUrl::GetApp(){
|
||||||
|
return app.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* RtmpUrl::GetStream(){
|
||||||
|
return stream.c_str();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_core_uri_hpp
|
||||||
|
#define _htl_core_uri_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_core_uri.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <http_parser.h>
|
||||||
|
|
||||||
|
class Uri
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Uri();
|
||||||
|
virtual ~Uri();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProtocolUrl : public Uri
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::string url;
|
||||||
|
http_parser_url hp_u;
|
||||||
|
protected:
|
||||||
|
std::string schema;
|
||||||
|
std::string host;
|
||||||
|
int port;
|
||||||
|
std::string path;
|
||||||
|
public:
|
||||||
|
ProtocolUrl();
|
||||||
|
virtual ~ProtocolUrl();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url);
|
||||||
|
public:
|
||||||
|
virtual const char* GetUrl();
|
||||||
|
virtual const char* GetSchema();
|
||||||
|
virtual const char* GetHost();
|
||||||
|
virtual int GetPort();
|
||||||
|
protected:
|
||||||
|
virtual std::string Get(http_parser_url_fields field);
|
||||||
|
/**
|
||||||
|
* get the parsed url field.
|
||||||
|
* @return return empty string if not set.
|
||||||
|
*/
|
||||||
|
static std::string GetUriField(std::string uri, http_parser_url* hp_u, http_parser_url_fields field);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpUrl : public ProtocolUrl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HttpUrl();
|
||||||
|
virtual ~HttpUrl();
|
||||||
|
public:
|
||||||
|
virtual std::string Resolve(std::string origin_url);
|
||||||
|
virtual HttpUrl* Copy();
|
||||||
|
public:
|
||||||
|
virtual const char* GetPath();
|
||||||
|
};
|
||||||
|
|
||||||
|
class RtmpUrl : public ProtocolUrl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string tcUrl;
|
||||||
|
std::string vhost;
|
||||||
|
std::string app;
|
||||||
|
std::string stream;
|
||||||
|
public:
|
||||||
|
RtmpUrl();
|
||||||
|
virtual ~RtmpUrl();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(std::string http_url);
|
||||||
|
public:
|
||||||
|
virtual const char* GetTcUrl();
|
||||||
|
virtual const char* GetVhost();
|
||||||
|
virtual const char* GetApp();
|
||||||
|
virtual const char* GetStream();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_stdinc_hpp
|
||||||
|
#define _htl_stdinc_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
// for 32bit os, 2G big file limit for unistd io,
|
||||||
|
// ie. read/write/lseek to use 64bits size for huge file.
|
||||||
|
#ifndef _FILE_OFFSET_BITS
|
||||||
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// for int64_t print using PRId64 format.
|
||||||
|
#ifndef __STDC_FORMAT_MACROS
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// project lib
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_hls_load.hpp>
|
||||||
|
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
|
||||||
|
#define DefaultDelaySeconds -1
|
||||||
|
#define DefaultHttpUrl "http://127.0.0.1:3080/hls/hls.m3u8"
|
||||||
|
#define DefaultVod false
|
||||||
|
|
||||||
|
int discovery_options(int argc, char** argv,
|
||||||
|
bool& show_help, bool& show_version, string& url, bool& vod, int& threads,
|
||||||
|
double& startup, double& delay, double& error, double& report, int& count)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
static option long_options[] = {
|
||||||
|
SharedOptions()
|
||||||
|
{"vod", no_argument, 0, 'o'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int option_index = 0;
|
||||||
|
while((opt = getopt_long(argc, argv, "hvc:r:t:os:d:e:m:", long_options, &option_index)) != -1){
|
||||||
|
switch(opt){
|
||||||
|
case 'o':
|
||||||
|
vod = true;
|
||||||
|
break;
|
||||||
|
ProcessSharedOptions()
|
||||||
|
default:
|
||||||
|
show_help = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check values
|
||||||
|
if(url == ""){
|
||||||
|
show_help = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(char** argv){
|
||||||
|
printf("%s, Copyright (c) 2013-2015 winlin\n", ProductHTTPName);
|
||||||
|
|
||||||
|
printf(""
|
||||||
|
"Usage: %s <Options> <-u URL>\n"
|
||||||
|
"%s base on st(state-threads), support huge concurrency.\n"
|
||||||
|
"Options:\n"
|
||||||
|
ShowHelpPart1()
|
||||||
|
" -o, --vod Whether url is vod, loop the m3u8 file list. default is %s\n"
|
||||||
|
ShowHelpPart2()
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
|
"Examples:\n"
|
||||||
|
"1. start a client\n"
|
||||||
|
" %s -c 1 -r %s\n"
|
||||||
|
"2. start 1000 clients\n"
|
||||||
|
" %s -c 1000 -r %s\n"
|
||||||
|
"3. start 10000 clients\n"
|
||||||
|
" %s -c 10000 -r %s\n"
|
||||||
|
"4. start 100000 clients\n"
|
||||||
|
" %s -c 100000 -r %s\n"
|
||||||
|
"5. start 10000 vod clients\n"
|
||||||
|
" %s -c 10000 -o -r %s\n"
|
||||||
|
"\n"
|
||||||
|
"This program built for %s.\n"
|
||||||
|
"Report bugs to <%s>\n",
|
||||||
|
argv[0], argv[0],
|
||||||
|
DefaultThread, DefaultHttpUrl, DefaultCount, // part1
|
||||||
|
(DefaultVod? "true":"false"), // vod
|
||||||
|
(double)DefaultStartupSeconds, (double)DefaultDelaySeconds, // part2
|
||||||
|
DefaultErrorSeconds, DefaultReportSeconds, // part2
|
||||||
|
argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl,
|
||||||
|
argv[0], DefaultHttpUrl,
|
||||||
|
BuildPlatform, BugReportEmail);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
bool show_help = false, show_version = false;
|
||||||
|
string url; bool vod = DefaultVod; int threads = DefaultThread;
|
||||||
|
double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds;
|
||||||
|
double report = DefaultReportSeconds; int count = DefaultCount;
|
||||||
|
|
||||||
|
if((ret = discovery_options(argc, argv, show_help, show_version, url, vod, threads, start, delay, error, report, count)) != ERROR_SUCCESS){
|
||||||
|
Error("discovery options failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Trace("params url=%s, vod=%d, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d",
|
||||||
|
url.c_str(), vod, threads, start, delay, error, report, count);
|
||||||
|
|
||||||
|
if(show_help){
|
||||||
|
help(argv);
|
||||||
|
}
|
||||||
|
if(show_version){
|
||||||
|
version();
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm farm;
|
||||||
|
|
||||||
|
if((ret = farm.Initialize(report)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize the farm failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < threads; i++){
|
||||||
|
StHlsTask* task = new StHlsTask();
|
||||||
|
|
||||||
|
if((ret = task->Initialize(url, vod, start, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = farm.Spawn(task)) != ERROR_SUCCESS){
|
||||||
|
Error("st farm spwan task failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
farm.WaitAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// project lib
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_http_load.hpp>
|
||||||
|
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
|
||||||
|
#define DefaultDelaySeconds 0.0
|
||||||
|
#define DefaultHttpUrl "http://127.0.0.1:3080/hls/segm130813144315787-522881.ts"
|
||||||
|
|
||||||
|
int discovery_options(int argc, char** argv,
|
||||||
|
bool& show_help, bool& show_version, string& url, int& threads,
|
||||||
|
double& startup, double& delay, double& error, double& report, int& count
|
||||||
|
){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
static option long_options[] = {
|
||||||
|
SharedOptions()
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int option_index = 0;
|
||||||
|
while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:", long_options, &option_index)) != -1){
|
||||||
|
switch(opt){
|
||||||
|
ProcessSharedOptions()
|
||||||
|
default:
|
||||||
|
show_help = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check values
|
||||||
|
if(url == ""){
|
||||||
|
show_help = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(char** argv){
|
||||||
|
printf("%s, Copyright (c) 2013-2015 winlin\n", ProductHTTPName);
|
||||||
|
|
||||||
|
printf(""
|
||||||
|
"Usage: %s <Options> <-u URL>\n"
|
||||||
|
"%s base on st(state-threads), support huge concurrency.\n"
|
||||||
|
"Options:\n"
|
||||||
|
ShowHelpPart1()
|
||||||
|
ShowHelpPart2()
|
||||||
|
"\n"
|
||||||
|
"Examples:\n"
|
||||||
|
"1. start a client\n"
|
||||||
|
" %s -c 1 -r %s\n"
|
||||||
|
"2. start 1000 clients\n"
|
||||||
|
" %s -c 1000 -r %s\n"
|
||||||
|
"3. start 10000 clients\n"
|
||||||
|
" %s -c 10000 -r %s\n"
|
||||||
|
"4. start 100000 clients\n"
|
||||||
|
" %s -c 100000 -r %s\n"
|
||||||
|
"\n"
|
||||||
|
"This program built for %s.\n"
|
||||||
|
"Report bugs to <%s>\n",
|
||||||
|
argv[0], argv[0],
|
||||||
|
DefaultThread, DefaultHttpUrl, DefaultCount, // part1
|
||||||
|
(double)DefaultStartupSeconds, DefaultDelaySeconds, // part2
|
||||||
|
DefaultErrorSeconds, DefaultReportSeconds, // part2
|
||||||
|
argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl, argv[0], DefaultHttpUrl,
|
||||||
|
BuildPlatform, BugReportEmail);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
bool show_help = false, show_version = false;
|
||||||
|
string url; int threads = DefaultThread;
|
||||||
|
double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds;
|
||||||
|
double report = DefaultReportSeconds; int count = DefaultCount;
|
||||||
|
|
||||||
|
if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count)) != ERROR_SUCCESS){
|
||||||
|
Error("discovery options failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d",
|
||||||
|
url.c_str(), threads, start, delay, error, report, count);
|
||||||
|
|
||||||
|
if(show_help){
|
||||||
|
help(argv);
|
||||||
|
}
|
||||||
|
if(show_version){
|
||||||
|
version();
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm farm;
|
||||||
|
|
||||||
|
if((ret = farm.Initialize(report)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize the farm failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < threads; i++){
|
||||||
|
StHttpTask* task = new StHttpTask();
|
||||||
|
|
||||||
|
if((ret = task->Initialize(url, start, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = farm.Spawn(task)) != ERROR_SUCCESS){
|
||||||
|
Error("st farm spwan task failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
farm.WaitAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// project lib
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_rtmp_load.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
|
||||||
|
#define DefaultDelaySeconds 1.0
|
||||||
|
#define DefaultRtmpUrl "rtmp://127.0.0.1:1935/live/livestream"
|
||||||
|
|
||||||
|
int discovery_options(int argc, char** argv,
|
||||||
|
bool& show_help, bool& show_version, string& url, int& threads,
|
||||||
|
double& startup, double& delay, double& error, double& report, int& count
|
||||||
|
){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
static option long_options[] = {
|
||||||
|
SharedOptions()
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int option_index = 0;
|
||||||
|
while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:", long_options, &option_index)) != -1){
|
||||||
|
switch(opt){
|
||||||
|
ProcessSharedOptions()
|
||||||
|
default:
|
||||||
|
show_help = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check values
|
||||||
|
if(url == ""){
|
||||||
|
show_help = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(char** argv){
|
||||||
|
printf("%s, Copyright (c) 2013-2015 winlin\n", ProductRtmpName);
|
||||||
|
printf("srs.librtmp %d.%d.%d (https://github.com/winlinvip/srs.librtmp)\n\n",
|
||||||
|
srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||||
|
|
||||||
|
printf(""
|
||||||
|
"Usage: %s <Options> <-u URL>\n"
|
||||||
|
"%s base on st(state-threads), support huge concurrency.\n"
|
||||||
|
"Options:\n"
|
||||||
|
ShowHelpPart1()
|
||||||
|
ShowHelpPart2()
|
||||||
|
"\n"
|
||||||
|
"Examples:\n"
|
||||||
|
"1. start a client\n"
|
||||||
|
" %s -c 1 -r %s\n"
|
||||||
|
"2. start 1000 clients\n"
|
||||||
|
" %s -c 1000 -r %s\n"
|
||||||
|
"3. start 10000 clients\n"
|
||||||
|
" %s -c 10000 -r %s\n"
|
||||||
|
"4. start 100000 clients\n"
|
||||||
|
" %s -c 100000 -r %s\n"
|
||||||
|
"\n"
|
||||||
|
"This program built for %s.\n"
|
||||||
|
"Report bugs to <%s>\n",
|
||||||
|
argv[0], argv[0],
|
||||||
|
DefaultThread, DefaultRtmpUrl, DefaultCount, // part1
|
||||||
|
(double)DefaultStartupSeconds, DefaultDelaySeconds, // part2
|
||||||
|
DefaultErrorSeconds, DefaultReportSeconds, // part2
|
||||||
|
argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl,
|
||||||
|
BuildPlatform, BugReportEmail);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
bool show_help = false, show_version = false;
|
||||||
|
string url; int threads = DefaultThread;
|
||||||
|
double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds;
|
||||||
|
double report = DefaultReportSeconds; int count = DefaultCount;
|
||||||
|
|
||||||
|
if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count)) != ERROR_SUCCESS){
|
||||||
|
Error("discovery options failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d",
|
||||||
|
url.c_str(), threads, start, delay, error, report, count);
|
||||||
|
|
||||||
|
if(show_help){
|
||||||
|
help(argv);
|
||||||
|
}
|
||||||
|
if(show_version){
|
||||||
|
version();
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm farm;
|
||||||
|
|
||||||
|
if((ret = farm.Initialize(report)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize the farm failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < threads; i++){
|
||||||
|
StRtmpTask* task = new StRtmpTask();
|
||||||
|
|
||||||
|
if((ret = task->Initialize(url, start, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = farm.Spawn(task)) != ERROR_SUCCESS){
|
||||||
|
Error("st farm spwan task failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
farm.WaitAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// project lib
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_rtmp_load.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
|
||||||
|
#define DefaultDelaySeconds 1.0
|
||||||
|
#define DefaultRtmpUrl "rtmp://127.0.0.1:1935/live/livestream"
|
||||||
|
|
||||||
|
int discovery_options(int argc, char** argv,
|
||||||
|
bool& show_help, bool& show_version, string& url, int& threads,
|
||||||
|
double& startup, double& delay, double& error, double& report, int& count
|
||||||
|
){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
static option long_options[] = {
|
||||||
|
SharedOptions()
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int option_index = 0;
|
||||||
|
while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:", long_options, &option_index)) != -1){
|
||||||
|
switch(opt){
|
||||||
|
ProcessSharedOptions()
|
||||||
|
default:
|
||||||
|
show_help = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check values
|
||||||
|
if(url == ""){
|
||||||
|
show_help = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(char** argv){
|
||||||
|
printf("%s, Copyright (c) 2013-2015 winlin\n", ProductRtmpName);
|
||||||
|
printf("srs.librtmp %d.%d.%d (https://github.com/winlinvip/srs.librtmp)\n\n",
|
||||||
|
srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||||
|
|
||||||
|
printf(""
|
||||||
|
"Usage: %s <Options> <-u URL>\n"
|
||||||
|
"%s base on st(state-threads), support huge concurrency.\n"
|
||||||
|
"Options:\n"
|
||||||
|
ShowHelpPart1()
|
||||||
|
ShowHelpPart2()
|
||||||
|
"\n"
|
||||||
|
"Examples:\n"
|
||||||
|
"1. start a client\n"
|
||||||
|
" %s -c 1 -r %s\n"
|
||||||
|
"2. start 1000 clients\n"
|
||||||
|
" %s -c 1000 -r %s\n"
|
||||||
|
"3. start 10000 clients\n"
|
||||||
|
" %s -c 10000 -r %s\n"
|
||||||
|
"4. start 100000 clients\n"
|
||||||
|
" %s -c 100000 -r %s\n"
|
||||||
|
"\n"
|
||||||
|
"This program built for %s.\n"
|
||||||
|
"Report bugs to <%s>\n",
|
||||||
|
argv[0], argv[0],
|
||||||
|
DefaultThread, DefaultRtmpUrl, DefaultCount, // part1
|
||||||
|
(double)DefaultStartupSeconds, DefaultDelaySeconds, // part2
|
||||||
|
DefaultErrorSeconds, DefaultReportSeconds, // part2
|
||||||
|
argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl, argv[0], DefaultRtmpUrl,
|
||||||
|
BuildPlatform, BugReportEmail);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
printf("WARNING!!!\n"
|
||||||
|
"WARNING!!! we use FAST algorithm to read tcp data directly, \n"
|
||||||
|
"WARNING!!! may not work for some RTMP server, it's ok for SRS/GO-SRS.\n"
|
||||||
|
"WARNING!!!\n\n");
|
||||||
|
|
||||||
|
bool show_help = false, show_version = false;
|
||||||
|
string url; int threads = DefaultThread;
|
||||||
|
double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds;
|
||||||
|
double report = DefaultReportSeconds; int count = DefaultCount;
|
||||||
|
|
||||||
|
if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count)) != ERROR_SUCCESS){
|
||||||
|
Error("discovery options failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d",
|
||||||
|
url.c_str(), threads, start, delay, error, report, count);
|
||||||
|
|
||||||
|
if(show_help){
|
||||||
|
help(argv);
|
||||||
|
}
|
||||||
|
if(show_version){
|
||||||
|
version();
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm farm;
|
||||||
|
|
||||||
|
if((ret = farm.Initialize(report)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize the farm failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < threads; i++){
|
||||||
|
StRtmpTaskFast* task = new StRtmpTaskFast();
|
||||||
|
|
||||||
|
if((ret = task->Initialize(url, start, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize task failed, url=%s, ret=%d", url.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = farm.Spawn(task)) != ERROR_SUCCESS){
|
||||||
|
Error("st farm spwan task failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
farm.WaitAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// project lib
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_app_rtmp_load.hpp>
|
||||||
|
#include <htl_app_rtmp_protocol.hpp>
|
||||||
|
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
|
||||||
|
#define DefaultDelaySeconds 1.0
|
||||||
|
#define DefaultRtmpUrlSingle "rtmp://127.0.0.1:1935/live/livestream"
|
||||||
|
#define DefaultRtmpUrl "rtmp://127.0.0.1:1935/live/livestream_{i}"
|
||||||
|
#define DefaultInputFlv "doc/source.200kbps.768x320.flv"
|
||||||
|
|
||||||
|
int discovery_options(int argc, char** argv,
|
||||||
|
bool& show_help, bool& show_version, string& url, int& threads,
|
||||||
|
double& startup, double& delay, double& error, double& report, int& count,
|
||||||
|
string& input
|
||||||
|
){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
static option long_options[] = {
|
||||||
|
SharedOptions()
|
||||||
|
{"input", no_argument, 0, 'i'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int option_index = 0;
|
||||||
|
while((opt = getopt_long(argc, argv, "hvc:r:t:s:d:e:m:i:", long_options, &option_index)) != -1){
|
||||||
|
switch(opt){
|
||||||
|
ProcessSharedOptions()
|
||||||
|
case 'i':
|
||||||
|
input = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
show_help = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check values
|
||||||
|
if(url == "" || input == ""){
|
||||||
|
show_help = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(char** argv){
|
||||||
|
printf("%s, Copyright (c) 2013-2015 winlin\n", ProductRtmpPublishName);
|
||||||
|
printf("srs.librtmp %d.%d.%d (https://github.com/winlinvip/srs.librtmp)\n\n",
|
||||||
|
srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||||
|
|
||||||
|
printf(""
|
||||||
|
"Usage: %s <Options> <-u URL>\n"
|
||||||
|
"%s base on st(state-threads), support huge concurrency.\n"
|
||||||
|
"Options:\n"
|
||||||
|
ShowHelpPart1()
|
||||||
|
ShowHelpPart2()
|
||||||
|
"\n"
|
||||||
|
"Examples:\n"
|
||||||
|
"1. start a client\n"
|
||||||
|
" %s -i %s -c 1 -r %s\n"
|
||||||
|
"2. start 1000 clients\n"
|
||||||
|
" %s -i %s -c 1000 -r %s\n"
|
||||||
|
"3. start 10000 clients\n"
|
||||||
|
" %s -i %s -c 10000 -r %s\n"
|
||||||
|
"4. start 100000 clients\n"
|
||||||
|
" %s -i %s -c 100000 -r %s\n"
|
||||||
|
"\n"
|
||||||
|
"This program built for %s.\n"
|
||||||
|
"Report bugs to <%s>\n",
|
||||||
|
argv[0], argv[0],
|
||||||
|
DefaultThread, DefaultRtmpUrl, DefaultCount, // part1
|
||||||
|
(double)DefaultStartupSeconds, DefaultDelaySeconds, // part2
|
||||||
|
DefaultErrorSeconds, DefaultReportSeconds, // part2
|
||||||
|
argv[0], DefaultInputFlv, DefaultRtmpUrlSingle,
|
||||||
|
argv[0], DefaultInputFlv, DefaultRtmpUrl,
|
||||||
|
argv[0], DefaultInputFlv, DefaultRtmpUrl,
|
||||||
|
argv[0], DefaultInputFlv, DefaultRtmpUrl,
|
||||||
|
BuildPlatform, BugReportEmail);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
bool show_help = false, show_version = false;
|
||||||
|
string url; int threads = DefaultThread;
|
||||||
|
double start = DefaultStartupSeconds, delay = DefaultDelaySeconds, error = DefaultErrorSeconds;
|
||||||
|
double report = DefaultReportSeconds; int count = DefaultCount;
|
||||||
|
string input;
|
||||||
|
|
||||||
|
if((ret = discovery_options(argc, argv, show_help, show_version, url, threads, start, delay, error, report, count, input)) != ERROR_SUCCESS){
|
||||||
|
Error("discovery options failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(show_help){
|
||||||
|
help(argv);
|
||||||
|
}
|
||||||
|
if(show_version){
|
||||||
|
version();
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace("params url=%s, threads=%d, start=%.2f, delay=%.2f, error=%.2f, report=%.2f, count=%d",
|
||||||
|
url.c_str(), threads, start, delay, error, report, count);
|
||||||
|
|
||||||
|
StFarm farm;
|
||||||
|
|
||||||
|
if((ret = farm.Initialize(report)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize the farm failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(int i = 0; i < threads; i++){
|
||||||
|
StRtmpPublishTask* task = new StRtmpPublishTask();
|
||||||
|
|
||||||
|
char index[16];
|
||||||
|
snprintf(index, sizeof(index), "%d", i);
|
||||||
|
|
||||||
|
std::string _index = index;
|
||||||
|
std::string rtmp_url = url;
|
||||||
|
size_t pos = std::string::npos;
|
||||||
|
if ((pos = rtmp_url.find("{i}")) != std::string::npos) {
|
||||||
|
rtmp_url = rtmp_url.replace(pos, pos + 3, _index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = task->Initialize(input, rtmp_url, start, delay, error, count)) != ERROR_SUCCESS){
|
||||||
|
Error("initialize task failed, input=%s, url=%s, ret=%d", input.c_str(), rtmp_url.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ret = farm.Spawn(task)) != ERROR_SUCCESS){
|
||||||
|
Error("st farm spwan task failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
farm.WaitAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_main_utility_hpp
|
||||||
|
#define _htl_main_utility_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_main_utility.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
// global instance for graceful log.
|
||||||
|
LogContext* context = new StLogContext();
|
||||||
|
|
||||||
|
#define DefaultThread 1
|
||||||
|
#define DefaultStartupSeconds 5.0
|
||||||
|
#define DefaultErrorSeconds 3.0
|
||||||
|
#define DefaultReportSeconds 30.0
|
||||||
|
#define DefaultCount 0
|
||||||
|
|
||||||
|
#define SharedOptions()\
|
||||||
|
{"clients", required_argument, 0, 'c'}, \
|
||||||
|
{"url", required_argument, 0, 'r'}, \
|
||||||
|
{"repeat", required_argument, 0, 't'}, \
|
||||||
|
\
|
||||||
|
{"startup", required_argument, 0, 's'}, \
|
||||||
|
{"delay", required_argument, 0, 'd'}, \
|
||||||
|
{"error", required_argument, 0, 'e'}, \
|
||||||
|
{"summary", required_argument, 0, 'm'}, \
|
||||||
|
\
|
||||||
|
{"help", no_argument, 0, 'h'}, \
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
|
||||||
|
#define ProcessSharedOptions()\
|
||||||
|
case 'h': \
|
||||||
|
show_help = true; \
|
||||||
|
break; \
|
||||||
|
case 'v': \
|
||||||
|
show_version = true; \
|
||||||
|
break; \
|
||||||
|
case 'c': \
|
||||||
|
threads = atoi(optarg); \
|
||||||
|
break; \
|
||||||
|
case 'r': \
|
||||||
|
url = optarg; \
|
||||||
|
break; \
|
||||||
|
case 't': \
|
||||||
|
count = atoi(optarg); \
|
||||||
|
break; \
|
||||||
|
case 's': \
|
||||||
|
startup = atof(optarg); \
|
||||||
|
break; \
|
||||||
|
case 'd': \
|
||||||
|
delay = atof(optarg); \
|
||||||
|
break; \
|
||||||
|
case 'e': \
|
||||||
|
error = atof(optarg); \
|
||||||
|
break; \
|
||||||
|
case 'm': \
|
||||||
|
report = atof(optarg); \
|
||||||
|
break;
|
||||||
|
|
||||||
|
#define ShowHelpPart1()\
|
||||||
|
" -c CLIENTS, --clients CLIENTS The concurrency client to start to request. defaut: %d\n" \
|
||||||
|
" -r URL, --url URL The load test url for each client to download/process. \n" \
|
||||||
|
" -t REPEAT, --repeat REPEAT The repeat is the number for each client to download the url. \n" \
|
||||||
|
" ie. %s\n" \
|
||||||
|
" default: %d. 0 means infinity.\n"
|
||||||
|
#define ShowHelpPart2()\
|
||||||
|
" -s STARTUP, --start STARTUP The start is the ramdom sleep when thread startup in seconds. \n" \
|
||||||
|
" defaut: %.2f. 0 means no delay.\n" \
|
||||||
|
" -d DELAY, --delay DELAY The delay is the ramdom sleep when success in seconds. \n" \
|
||||||
|
" default: %.2f. 0 means no delay. -1 means to use HLS EXTINF duration(HLS only).\n" \
|
||||||
|
" -e ERROR, --error ERROR The error is the sleep when error in seconds. \n" \
|
||||||
|
" defaut: %.2f. 0 means no delay. \n" \
|
||||||
|
" -m SUMMARY, --summary SUMMARY The summary is the sleep when report in seconds. \n" \
|
||||||
|
" etasks is error_tasks, statks is sub_tasks, estatks is error_sub_tasks.\n" \
|
||||||
|
" duration is the running time in seconds, tduration is the avarage duation of tasks.\n" \
|
||||||
|
" nread/nwrite in Mbps, duration/tduration in seconds.\n" \
|
||||||
|
" defaut: %.2f. 0 means no delay. \n" \
|
||||||
|
" -v, --version Print the version and exit.\n" \
|
||||||
|
" -h, --help Print this help message and exit.\n"
|
||||||
|
|
||||||
|
void version(){
|
||||||
|
printf(ProductVersion);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,513 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <htl_stdinc.hpp>
|
||||||
|
|
||||||
|
// system
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// socket
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
// dns
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <htl_core_error.hpp>
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
|
||||||
|
StStatistic::StStatistic(){
|
||||||
|
starttime = StUtility::GetCurrentTime();
|
||||||
|
task_duration = 0;
|
||||||
|
threads = alive = 0;
|
||||||
|
nread = nwrite = 0;
|
||||||
|
tasks = err_tasks = sub_tasks = err_sub_tasks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
StStatistic::~StStatistic(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnRead(int /*tid*/, ssize_t nread_bytes){
|
||||||
|
this->nread += nread_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnWrite(int /*tid*/, ssize_t nwrite_bytes){
|
||||||
|
this->nwrite += nwrite_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnThreadRun(int /*tid*/){
|
||||||
|
threads++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnThreadQuit(int /*tid*/){
|
||||||
|
threads--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnTaskStart(int /*tid*/, std::string /*task_url*/){
|
||||||
|
alive++;
|
||||||
|
tasks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnTaskError(int /*tid*/, int duration_seconds){
|
||||||
|
alive--;
|
||||||
|
err_tasks++;
|
||||||
|
this->task_duration += duration_seconds * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnTaskEnd(int /*tid*/, int duration_seconds){
|
||||||
|
alive--;
|
||||||
|
this->task_duration += duration_seconds * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnSubTaskStart(int /*tid*/, std::string /*sub_task_url*/){
|
||||||
|
sub_tasks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnSubTaskError(int /*tid*/, int duration_seconds){
|
||||||
|
err_sub_tasks++;
|
||||||
|
this->task_duration += duration_seconds * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::OnSubTaskEnd(int /*tid*/, int duration_seconds){
|
||||||
|
this->task_duration += duration_seconds * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StStatistic::DoReport(double sleep_ms){
|
||||||
|
for(;;){
|
||||||
|
int64_t duration = StUtility::GetCurrentTime() - starttime;
|
||||||
|
double read_mbps = 0, write_mbps = 0;
|
||||||
|
|
||||||
|
if(duration > 0){
|
||||||
|
read_mbps = nread * 8.0 / duration / 1000;
|
||||||
|
write_mbps = nwrite * 8.0 / duration / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
double avarage_duration = task_duration/1000.0;
|
||||||
|
if(tasks > 0){
|
||||||
|
avarage_duration /= tasks;
|
||||||
|
}
|
||||||
|
LReport("[report] [%d] threads:%d alive:%d duration:%.0f tduration:%.0f nread:%.2f nwrite:%.2f "
|
||||||
|
"tasks:%"PRId64" etasks:%"PRId64" stasks:%"PRId64" estasks:%"PRId64,
|
||||||
|
getpid(), threads, alive, duration/1000.0, avarage_duration, read_mbps, write_mbps,
|
||||||
|
tasks, err_tasks, sub_tasks, err_sub_tasks);
|
||||||
|
|
||||||
|
st_usleep((st_utime_t)(sleep_ms * 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StStatistic* statistic = new StStatistic();
|
||||||
|
|
||||||
|
StTask::StTask(){
|
||||||
|
static int _id = 0;
|
||||||
|
id = ++_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
StTask::~StTask(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StTask::GetId(){
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm::StFarm(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StFarm::~StFarm(){
|
||||||
|
}
|
||||||
|
|
||||||
|
int StFarm::Initialize(double report){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
report_seconds = report;
|
||||||
|
|
||||||
|
// use linux epoll.
|
||||||
|
if(st_set_eventsys(ST_EVENTSYS_ALT) == -1){
|
||||||
|
ret = ERROR_ST_INITIALIZE;
|
||||||
|
Error("st_set_eventsys use linux epoll failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(st_init() != 0){
|
||||||
|
ret = ERROR_ST_INITIALIZE;
|
||||||
|
Error("st_init failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StUtility::InitRandom();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StFarm::Spawn(StTask* task){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(st_thread_create(st_thread_function, task, 0, 0) == NULL){
|
||||||
|
ret = ERROR_ST_THREAD_CREATE;
|
||||||
|
Error("crate st_thread failed, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace("create thread for task #%d success", task->GetId());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StFarm::WaitAll(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// main thread turn to a report therad.
|
||||||
|
statistic->DoReport(report_seconds * 1000);
|
||||||
|
|
||||||
|
st_thread_exit(NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* StFarm::st_thread_function(void* args){
|
||||||
|
StTask* task = (StTask*)args;
|
||||||
|
|
||||||
|
context->SetId(task->GetId());
|
||||||
|
|
||||||
|
statistic->OnThreadRun(task->GetId());
|
||||||
|
|
||||||
|
int ret = task->Process();
|
||||||
|
|
||||||
|
statistic->OnThreadQuit(task->GetId());
|
||||||
|
|
||||||
|
if(ret != ERROR_SUCCESS){
|
||||||
|
Warn("st task terminate with ret=%d", ret);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Trace("st task terminate with ret=%d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete task;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
StSocket::StSocket(){
|
||||||
|
sock_nfd = NULL;
|
||||||
|
status = SocketInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
StSocket::~StSocket(){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
st_netfd_t StSocket::GetStfd(){
|
||||||
|
return sock_nfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketStatus StSocket::Status(){
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Connect(const char* ip, int port){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
Close();
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(sock == -1){
|
||||||
|
ret = ERROR_SOCKET;
|
||||||
|
Error("create socket error. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reuse_socket = 1;
|
||||||
|
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1){
|
||||||
|
ret = ERROR_SOCKET;
|
||||||
|
Error("setsockopt reuse-addr error. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int keepalive_socket = 1;
|
||||||
|
if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive_socket, sizeof(int)) == -1){
|
||||||
|
ret = ERROR_SOCKET;
|
||||||
|
Error("setsockopt keep-alive error. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_nfd = st_netfd_open_socket(sock);
|
||||||
|
if(sock_nfd == NULL){
|
||||||
|
ret = ERROR_OPEN_SOCKET;
|
||||||
|
Error("st_netfd_open_socket failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("create socket(%d) success", sock);
|
||||||
|
|
||||||
|
|
||||||
|
// connect to server
|
||||||
|
sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = inet_addr(ip);
|
||||||
|
|
||||||
|
if(st_connect(sock_nfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){
|
||||||
|
ret = ERROR_CONNECT;
|
||||||
|
Error("connect to server(%s:%d) error. ret=%d", ip, port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Info("connec to server %s at port %d success", ip, port);
|
||||||
|
|
||||||
|
status = SocketConnected;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Read(const void* buf, size_t size, ssize_t* nread){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ssize_t got = st_read(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
// On success a non-negative integer indicating the number of bytes actually read is returned
|
||||||
|
// (a value of 0 means the network connection is closed or end of file is reached).
|
||||||
|
if(got <= 0){
|
||||||
|
if(got == 0){
|
||||||
|
errno = ECONNRESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ERROR_READ;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(got > 0){
|
||||||
|
statistic->OnRead(context->GetId(), got);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread) {
|
||||||
|
*nread = got;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Readv(const iovec *iov, int iov_size, ssize_t* nread){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ssize_t got = st_readv(sock_nfd, iov, iov_size, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
// On success a non-negative integer indicating the number of bytes actually read is returned
|
||||||
|
// (a value of 0 means the network connection is closed or end of file is reached).
|
||||||
|
if(got <= 0){
|
||||||
|
if(got == 0){
|
||||||
|
errno = ECONNRESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ERROR_READ;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(got > 0){
|
||||||
|
statistic->OnRead(context->GetId(), got);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread) {
|
||||||
|
*nread = got;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::ReadFully(const void* buf, size_t size, ssize_t* nread){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ssize_t got = st_read_fully(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
// On success a non-negative integer indicating the number of bytes actually read is returned
|
||||||
|
// (a value less than nbyte means the network connection is closed or end of file is reached)
|
||||||
|
if(got != (ssize_t)size){
|
||||||
|
if(got >= 0){
|
||||||
|
errno = ECONNRESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ERROR_READ;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(got > 0){
|
||||||
|
statistic->OnRead(context->GetId(), got);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread) {
|
||||||
|
*nread = got;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Write(const void* buf, size_t size, ssize_t* nwrite){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ssize_t writen = st_write(sock_nfd, (void*)buf, size, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
if(writen <= 0){
|
||||||
|
ret = ERROR_SEND;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(writen > 0){
|
||||||
|
statistic->OnWrite(context->GetId(), writen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nwrite) {
|
||||||
|
*nwrite = writen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Writev(const iovec *iov, int iov_size, ssize_t* nwrite){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ssize_t writen = st_writev(sock_nfd, iov, iov_size, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
if(writen <= 0){
|
||||||
|
ret = ERROR_SEND;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(writen > 0){
|
||||||
|
statistic->OnWrite(context->GetId(), writen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nwrite) {
|
||||||
|
*nwrite = writen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StSocket::Close(){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(sock_nfd == NULL){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = st_netfd_fileno(sock_nfd);
|
||||||
|
if(st_netfd_close(sock_nfd) != 0){
|
||||||
|
ret = ERROR_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_nfd = NULL;
|
||||||
|
status = SocketDisconnected;
|
||||||
|
|
||||||
|
::close(fd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t StUtility::GetCurrentTime(){
|
||||||
|
timeval now;
|
||||||
|
|
||||||
|
int ret = gettimeofday(&now, NULL);
|
||||||
|
|
||||||
|
if(ret == -1){
|
||||||
|
Warn("gettimeofday error, ret=%d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we must convert the tv_sec/tv_usec to int64_t.
|
||||||
|
return ((int64_t)now.tv_sec)*1000 + ((int64_t)now.tv_usec) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StUtility::InitRandom(){
|
||||||
|
timeval now;
|
||||||
|
|
||||||
|
if(gettimeofday(&now, NULL) == -1){
|
||||||
|
srand(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
srand(now.tv_sec * 1000000 + now.tv_usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
st_utime_t StUtility::BuildRandomMTime(double sleep_seconds){
|
||||||
|
if(sleep_seconds <= 0){
|
||||||
|
return 0 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 80% consts value.
|
||||||
|
// 40% random value.
|
||||||
|
// to get more graceful random time to mocking HLS client.
|
||||||
|
st_utime_t sleep_ms = (int)(sleep_seconds * 1000 * 0.7) + rand() % (int)(sleep_seconds * 1000 * 0.4);
|
||||||
|
|
||||||
|
return sleep_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StUtility::DnsResolve(string host, string& ip){
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if(inet_addr(host.c_str()) != INADDR_NONE){
|
||||||
|
ip = host;
|
||||||
|
Info("dns resolve %s to %s", host.c_str(), ip.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostent* answer = gethostbyname(host.c_str());
|
||||||
|
if(answer == NULL){
|
||||||
|
ret = ERROR_DNS_RESOLVE;
|
||||||
|
Error("dns resolve host %s error. ret=%d", host.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ipv4[16];
|
||||||
|
memset(ipv4, 0, sizeof(ipv4));
|
||||||
|
for(int i = 0; i < answer->h_length; i++){
|
||||||
|
inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
|
||||||
|
Info("dns resolve host %s to %s.", host.c_str(), ipv4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = ipv4;
|
||||||
|
Info("dns resolve %s to %s", host.c_str(), ip.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StLogContext::StLogContext(){
|
||||||
|
}
|
||||||
|
|
||||||
|
StLogContext::~StLogContext(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void StLogContext::SetId(int id){
|
||||||
|
cache[st_thread_self()] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StLogContext::GetId(){
|
||||||
|
return cache[st_thread_self()];
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _htl_os_st_hpp
|
||||||
|
#define _htl_os_st_hpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <htl_os_st.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <st.h>
|
||||||
|
|
||||||
|
#include <htl_core_log.hpp>
|
||||||
|
|
||||||
|
// the statistic for each st-thread(task)
|
||||||
|
class StStatistic
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int threads, alive;
|
||||||
|
int64_t starttime, task_duration;
|
||||||
|
int64_t nread, nwrite;
|
||||||
|
int64_t tasks, err_tasks, sub_tasks, err_sub_tasks;
|
||||||
|
public:
|
||||||
|
StStatistic();
|
||||||
|
virtual ~StStatistic();
|
||||||
|
public:
|
||||||
|
virtual void OnRead(int tid, ssize_t nread_bytes);
|
||||||
|
virtual void OnWrite(int tid, ssize_t nwrite_bytes);
|
||||||
|
public:
|
||||||
|
// when task thread run.
|
||||||
|
virtual void OnThreadRun(int tid);
|
||||||
|
// when task thread quit.
|
||||||
|
virtual void OnThreadQuit(int tid);
|
||||||
|
// when task start request url, ie. get m3u8
|
||||||
|
virtual void OnTaskStart(int tid, std::string task_url);
|
||||||
|
// when task error.
|
||||||
|
virtual void OnTaskError(int tid, int duration_seconds);
|
||||||
|
// when task finish request url, ie. finish all ts in m3u8
|
||||||
|
virtual void OnTaskEnd(int tid, int duration_seconds);
|
||||||
|
// when sub task start, ie. get ts in m3u8
|
||||||
|
virtual void OnSubTaskStart(int tid, std::string sub_task_url);
|
||||||
|
// when sub task error.
|
||||||
|
virtual void OnSubTaskError(int tid, int duration_seconds);
|
||||||
|
// when sub task finish, ie. finish a ts.
|
||||||
|
virtual void OnSubTaskEnd(int tid, int duration_seconds);
|
||||||
|
public:
|
||||||
|
virtual void DoReport(double sleep_ms);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern StStatistic* statistic;
|
||||||
|
|
||||||
|
// abstract task for st, which run in a st-thread.
|
||||||
|
class StTask
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int id;
|
||||||
|
public:
|
||||||
|
StTask();
|
||||||
|
virtual ~StTask();
|
||||||
|
public:
|
||||||
|
virtual int GetId();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* the framework will start a thread for the task,
|
||||||
|
* then invoke the Process function to do actual transaction.
|
||||||
|
*/
|
||||||
|
virtual int Process() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// the farm for all StTask, to spawn st-thread and process all task.
|
||||||
|
class StFarm
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
double report_seconds;
|
||||||
|
public:
|
||||||
|
StFarm();
|
||||||
|
virtual ~StFarm();
|
||||||
|
public:
|
||||||
|
virtual int Initialize(double report);
|
||||||
|
virtual int Spawn(StTask* task);
|
||||||
|
virtual int WaitAll();
|
||||||
|
private:
|
||||||
|
static void* st_thread_function(void* args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// the socket status
|
||||||
|
enum SocketStatus{
|
||||||
|
SocketInit,
|
||||||
|
SocketConnected,
|
||||||
|
SocketDisconnected,
|
||||||
|
};
|
||||||
|
|
||||||
|
// the socket base on st.
|
||||||
|
class StSocket
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SocketStatus status;
|
||||||
|
st_netfd_t sock_nfd;
|
||||||
|
public:
|
||||||
|
StSocket();
|
||||||
|
virtual ~StSocket();
|
||||||
|
public:
|
||||||
|
virtual st_netfd_t GetStfd();
|
||||||
|
virtual SocketStatus Status();
|
||||||
|
virtual int Connect(const char* ip, int port);
|
||||||
|
virtual int Read(const void* buf, size_t size, ssize_t* nread);
|
||||||
|
virtual int Readv(const iovec *iov, int iov_size, ssize_t* nread);
|
||||||
|
virtual int ReadFully(const void* buf, size_t size, ssize_t* nread);
|
||||||
|
virtual int Write(const void* buf, size_t size, ssize_t* nwrite);
|
||||||
|
virtual int Writev(const iovec *iov, int iov_size, ssize_t* nwrite);
|
||||||
|
virtual int Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
// common utilities.
|
||||||
|
class StUtility
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static int64_t GetCurrentTime();
|
||||||
|
static void InitRandom();
|
||||||
|
static st_utime_t BuildRandomMTime(double sleep_seconds);
|
||||||
|
static int DnsResolve(std::string host, std::string& ip);
|
||||||
|
};
|
||||||
|
|
||||||
|
// st-thread based log context.
|
||||||
|
class StLogContext : public LogContext
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::map<st_thread_t, int> cache;
|
||||||
|
public:
|
||||||
|
StLogContext();
|
||||||
|
virtual ~StLogContext();
|
||||||
|
public:
|
||||||
|
virtual void SetId(int id);
|
||||||
|
virtual int GetId();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,3 @@
|
||||||
|
#ifndef _st_load_icpp_init_stub
|
||||||
|
#define _st_load_icpp_init_stub
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
file
|
||||||
|
main readonly separator,
|
||||||
|
..\main\htl_main_hls_load.cpp,
|
||||||
|
..\main\htl_main_http_load.cpp,
|
||||||
|
..\main\htl_main_rtmp_load.cpp,
|
||||||
|
..\main\htl_main_rtmp_load_fast.cpp,
|
||||||
|
..\main\htl_main_rtmp_publish.cpp,
|
||||||
|
..\main\htl_main_utility.cpp,
|
||||||
|
..\main\htl_main_utility.hpp,
|
||||||
|
app readonly separator,
|
||||||
|
..\app\htl_app_hls_load.cpp,
|
||||||
|
..\app\htl_app_hls_load.hpp,
|
||||||
|
..\app\htl_app_http_load.cpp,
|
||||||
|
..\app\htl_app_http_load.hpp,
|
||||||
|
..\app\htl_app_m3u8_parser.cpp,
|
||||||
|
..\app\htl_app_m3u8_parser.hpp,
|
||||||
|
..\app\htl_app_rtmp_load.cpp,
|
||||||
|
..\app\htl_app_rtmp_load.hpp,
|
||||||
|
..\app\htl_app_rtmp_play.cpp,
|
||||||
|
..\app\htl_app_rtmp_play.hpp,
|
||||||
|
..\app\htl_app_rtmp_protocol.cpp,
|
||||||
|
..\app\htl_app_rtmp_protocol.hpp,
|
||||||
|
..\app\htl_app_rtmp_publish.cpp,
|
||||||
|
..\app\htl_app_rtmp_publish.hpp,
|
||||||
|
..\app\htl_app_srs_hijack.cpp,
|
||||||
|
..\app\htl_app_srs_hijack.hpp,
|
||||||
|
..\app\htl_app_task_base.cpp,
|
||||||
|
..\app\htl_app_task_base.hpp,
|
||||||
|
..\app\htl_app_http_client.cpp,
|
||||||
|
..\app\htl_app_http_client.hpp,
|
||||||
|
core readonly separator,
|
||||||
|
..\core\htl_stdinc.hpp,
|
||||||
|
..\core\htl_core_error.cpp,
|
||||||
|
..\core\htl_core_error.hpp,
|
||||||
|
..\core\htl_core_log.cpp,
|
||||||
|
..\core\htl_core_log.hpp,
|
||||||
|
..\core\htl_core_uri.cpp,
|
||||||
|
..\core\htl_core_uri.hpp,
|
||||||
|
..\core\htl_core_aggregate_ret.cpp,
|
||||||
|
..\core\htl_core_aggregate_ret.hpp,
|
||||||
|
os readonly separator,
|
||||||
|
..\os\htl_os_st.cpp,
|
||||||
|
..\os\htl_os_st.hpp,
|
||||||
|
st-1.9 readonly separator,
|
||||||
|
..\..\objs\st-1.9\obj\st.h,
|
||||||
|
http-parser readonly separator,
|
||||||
|
..\..\objs\http-parser-2.1\http_parser.h,
|
||||||
|
..\..\objs\http-parser-2.1\http_parser.c;
|
||||||
|
|
||||||
|
mainconfig
|
||||||
|
"main" = "MAIN";
|
||||||
|
|
Loading…
Reference in New Issue