├── .gitignore ├── README.md ├── ci ├── vagrant_ci.sh ├── x100speed_transcode.service └── x100speed_transcode_ci.sh ├── docs ├── API_Reference.md ├── Redis_Storage_Design.md └── md5_algorithm.md ├── sources ├── .gitignore ├── README.md ├── admin │ └── admin.py ├── conf │ └── transcoder.conf ├── interface │ ├── tests │ │ └── x100speed_interface_test.py │ └── x100speed_interface.py ├── mediainfowrapper │ ├── README.md │ ├── m.py │ └── mediainfo.py ├── run.py ├── service_report │ ├── README.md │ ├── conf │ │ └── service.conf │ └── service_report.py ├── setup.py ├── static │ ├── css │ │ └── x100speed.css │ ├── js │ │ └── x100toolkit.js │ ├── lib │ │ ├── bootstrap.min.css │ │ ├── hls │ │ │ ├── browser.js │ │ │ ├── lib │ │ │ │ ├── async.js │ │ │ │ ├── jbinary.js │ │ │ │ ├── jdataview.js │ │ │ │ └── require.js │ │ │ ├── mpegts_to_mp4 │ │ │ │ ├── adts.js │ │ │ │ ├── h264.js │ │ │ │ ├── index.js │ │ │ │ ├── mp4.js │ │ │ │ ├── mpegts.js │ │ │ │ └── pes.js │ │ │ ├── shim │ │ │ │ ├── console.time.js │ │ │ │ └── console.worker.js │ │ │ └── worker.js │ │ ├── jquery-1.11.3.min.js │ │ ├── jquery-2.1.4.min.js │ │ └── jquery.qrcode.min.js │ ├── play.html │ ├── play_h5.html │ └── upload.html ├── transcode_origin_file │ ├── conf │ │ └── transcode_origin_file.conf │ ├── data │ │ └── ywOVs9MIA8AUUMSSuI_16.ts │ └── transcode_origin_file.py ├── transcoder │ ├── data │ │ └── test.ts │ ├── test │ │ └── transcoder_test.py │ ├── transcoder.py │ └── transcoder_test.py ├── x100monitor │ ├── README.md │ ├── conf │ │ └── monitor.conf │ ├── monitor_client.py │ ├── setup.py │ ├── test.py │ └── x100monitor.py └── x100utils │ ├── x100config.py │ ├── x100http.py │ ├── x100redis.py │ ├── x100request.py │ └── x100util.py ├── tools ├── burndown_drawer.pl ├── fake_m3u8.py └── file.png └── workspace ├── mpegts ├── Makefile ├── cku.ts ├── common.c ├── gmpegts.c ├── pid_pat ├── x100mpegts.h ├── x100mpegts_alloc.c └── x100mpegts_debug.c ├── psutil_test.py ├── upload_page └── x100uploadpage │ ├── README.md │ ├── __pycache__ │ ├── http.cpython-34.pyc │ ├── testfile.cpython-34.pyc │ └── transcoder.cpython-34.pyc │ ├── bootstrap.min.css │ ├── conf │ └── transcoder.conf │ ├── css │ └── x100speed.css │ ├── http_request.py │ ├── index.html │ ├── jquery-2.1.4.min.js │ ├── jquery.qrcode.min.js │ ├── lib │ ├── bootstrap.min.css │ ├── hls │ │ ├── browser.js │ │ ├── lib │ │ │ ├── async.js │ │ │ ├── jbinary.js │ │ │ ├── jdataview.js │ │ │ └── require.js │ │ ├── mpegts_to_mp4 │ │ │ ├── adts.js │ │ │ ├── h264.js │ │ │ ├── index.js │ │ │ ├── mp4.js │ │ │ ├── mpegts.js │ │ │ └── pes.js │ │ ├── shim │ │ │ ├── console.time.js │ │ │ └── console.worker.js │ │ └── worker.js │ ├── jquery-1.11.3.min.js │ └── x100toolkit.js │ ├── play.html │ ├── process_info.py │ ├── run.py │ ├── transcoder.py │ ├── transcoder.py.bak │ ├── upload.html │ ├── upload.py │ ├── x100 │ ├── __pycache__ │ │ ├── x100config.cpython-34.pyc │ │ ├── x100http.cpython-34.pyc │ │ ├── x100request.cpython-34.pyc │ │ └── x100util.cpython-34.pyc │ ├── x100config.py │ ├── x100http.py │ ├── x100redis.py │ ├── x100request.py │ └── x100util.py │ └── x100speed.css └── while.py /.gitignore: -------------------------------------------------------------------------------- 1 | */__pycache__ 2 | */*/__pycache__ 3 | */*/*/__pycache__ 4 | */*/*/*/__pycache__ 5 | */*/*/*/*/__pycache__ 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | env/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ######PAUSED @ 2015.9.30###### 2 | 3 | VideoStack:构建视频云服务的开源软件 4 | ==================================== 5 | 6 | 7 | VideoStack是什么? 8 | ----------------- 9 | 10 | 1. VideoStack解决互联网视频上传、转码、封装、存储、分发、缓存以及随之而来的集群管理问题; 11 | 12 | 2. 为大并发视频直播以及处理海量点播内容而设计; 13 | 14 | 3. 不断追踪浏览器技术以及视频技术进展,VideoStack尽量采用最新的技术以优化用户体验; 15 | 16 | 4. 提供各平台上的浏览器视频播放,但不包括播放器软件或是播放App的实现; 17 | 18 | 5. VideoStack可以通过后台Web管理界面或VideoStackAPI来控制; 19 | 20 | 21 | VideoStack开发方式是什么样的? 22 | ----------------------------- 23 | 24 | 1. VideoStack使用大量的,包括Python、FFmpeg、Varnish在内的开源/免费软件,使它们成为理想的基础设施。VideoStack不重复解决优秀软件已经解决的问题; 25 | 26 | 2. 发布周期为半年,期间穿插频繁的里程碑; 27 | 28 | 3. 可以抽象出来的部分作为独立的项目来维护,如x100http、x100redis2mysql、x100FFmpegWrapper。 29 | 30 | 31 | VideoStack开发完成了吗? 32 | ----------------------- 33 | 34 | 1. 它的版本号还没到1.0。但我们仔细设计和实现了VideoStack中现有的功能,这些已经发布的功能可以完美地工作; 35 | 36 | 2. 在后续的开发中,我们会仔细考虑旧版本API的兼容问题,尽量不破坏它。 37 | 38 | 39 | VideoStack使用了哪些开源/免费的软件项目? 40 | ----------------------------------------- 41 | 42 | VideoStack使用了以下开源项目,我们对它们的编写者们表示由衷感谢。 43 | 44 | FFmpeg - http://ffmpeg.org 45 | 46 | Varnish - https://www.varnish-cache.org 47 | 48 | Python - https://www.python.org 49 | 50 | Redis - http://redis.io 51 | 52 | MySQL - https://www.mysql.com 53 | -------------------------------------------------------------------------------- /ci/vagrant_ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # author : renpeng 4 | # github : https://github.com/laodifang 5 | # description : vagrant ci 6 | # date : 2015-08-05 7 | 8 | vagrant_box='/home/software/centos7_VirtualBoxVMs5.0.box' 9 | ci_box_name='x100speed_transcode' 10 | prefix_name='x100speed_transcode_' 11 | create_date=`date +%Y%m%d-%H%M%S` 12 | ci_system_name=${prefix_name}${create_date} 13 | 14 | box_list=`vagrant box list |grep x100speed_transcode` 15 | if [ -z "$box_list" ]; then 16 | vagrant box add $ci_box_name $vagrant_box 17 | fi 18 | 19 | #create ci system 20 | mkdir -p $ci_system_name 21 | cd $ci_system_name 22 | 23 | vagrant init $ci_box_name 24 | 25 | sed -i 's/^ # config.vm.network "forwarded_port".*/ config\.vm\.network "forwarded_port", guest: 80, host: 80/g' Vagrantfile 26 | sed -i 's/^ # config\.vm\.provision.*/ config\.vm\.provision "shell", path: "\.\.\/x100speed_transcode_ci.sh"/g' Vagrantfile 27 | 28 | vagrant up 29 | -------------------------------------------------------------------------------- /ci/x100speed_transcode.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=x100speed video transcode 3 | After=network.target 4 | 5 | [Service] 6 | Type=forking 7 | ExecStart=/usr/local/bin/python3 /data/x100speed_transcode/sources/run.py 8 | PrivateTmp=true 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | 13 | -------------------------------------------------------------------------------- /ci/x100speed_transcode_ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # author : renpeng 4 | # github : https://github.com/laodifang 5 | # description : x100speed transcode ci 6 | # date : 2015-08-05 7 | 8 | INSTALL_PACKAGET_PATH='/data/install' 9 | PACKAGE_URL='http://videostack.org/package/' 10 | #PACKAGE_URL='http://10.221.193.64:8000/' 11 | X100SPEED_TRANSCODE_PACKAGET_URL='http://10.221.193.64:8000/' 12 | 13 | # python3 14 | PYTHON3_URL="${PACKAGE_URL}Python-3.4.3.tgz" 15 | PYTHON3_PACKET='Python-3.4.3.tgz' 16 | PYTHON3_NAME='Python-3.4.3' 17 | PYTHON3_BIN_PATH='/usr/local/bin/python3' 18 | 19 | # ffmpeg 20 | FFMPEG_URL="${PACKAGE_URL}ffmpeg_install_2.7.2.tar.gz" 21 | FFMPEG_PACKET='ffmpeg_install_2.7.2.tar.gz' 22 | FFMPEG_NAME='ffmpeg_install_2.7.2' 23 | FFMPEG_BIN_PATH='/usr/bin/ffmpeg' 24 | 25 | # redis 26 | REDIS_URL='' 27 | REDIS_PACKET='' 28 | REDIS_NAME='' 29 | REDIS_BIN_PATH='/usr/bin/redis-server' 30 | 31 | # require python3 module 32 | REQUIRE_PYTHON3_MODULE='redis x100http x100idgen x100daemon x100mpegts DataStructuresSerialized' 33 | PIP3_SOURCE='http://pypi.douban.com/simple' 34 | 35 | # app 36 | APP_PATH='/data' 37 | X100SPEED_TRANSCODE_URL="${X100SPEED_TRANSCODE_PACKAGET_URL}x100speed_transcode.tar.gz" 38 | X100SPEED_TRANSCODE_PACKET='x100speed_transcode.tar.gz' 39 | X100SPEED_TRANSCODE_NAME='x100speed_transcode' 40 | 41 | 42 | # disable firewalld 43 | sudo sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config 44 | sudo systemctl stop firewalld 45 | sudo systemctl disable firewalld 46 | sudo setenforce 0 47 | 48 | # init server 49 | sudo yum -y install epel-release git openssl openssl-devel perl-devel 50 | sudo wget http://xrl.us/cpanm --no-check-certificate -O /sbin/cpanm 51 | sudo chmod +x /sbin/cpanm 52 | 53 | if [ ! -s '/sbin/cpanm' ]; then 54 | echo "!!!!!! cpanm install failed !!!!!!" 55 | exit 0 56 | fi 57 | 58 | sudo mkdir -p ${INSTALL_PACKAGET_PATH} 59 | cd ${INSTALL_PACKAGET_PATH} 60 | 61 | # install python3 62 | sudo wget ${PYTHON3_URL} 63 | sudo tar zxvf ${PYTHON3_PACKET} 64 | cd ${PYTHON3_NAME} 65 | sudo ./configure 66 | sudo make 67 | sudo make install 68 | cd ../ 69 | 70 | python3_test=`sudo ${PYTHON3_BIN_PATH} -V` 71 | if [ -z "$python3_test" ]; then 72 | echo "!!!!!! python3 install failed !!!!!!" 73 | exit 0 74 | fi 75 | 76 | # install ffmpeg 77 | sudo cpanm Digest::MD5 78 | sudo wget ${FFMPEG_URL} 79 | sudo tar zxvf ${FFMPEG_PACKET} 80 | cd ${FFMPEG_NAME} 81 | sudo sh install_ffmpeg.sh 82 | cd ../ 83 | 84 | ffmpeg_test=`sudo ${FFMPEG_BIN_PATH} -version` 85 | if [ -z "$ffmpeg_test" ]; then 86 | echo "!!!!!! ffmpeg install failed !!!!!!" 87 | exit 0 88 | fi 89 | 90 | # install redis 91 | sudo yum -y install redis 92 | sudo sed -i 's/^daemonize no$/daemonize yes/g' /etc/redis.conf 93 | sudo sed -i 's/^# requirepass foobared$/requirepass foobared/g' /etc/redis.conf 94 | sudo systemctl enable redis 95 | 96 | redis_server_test=`sudo ${REDIS_BIN_PATH} -v` 97 | if [ -z "$redis_server_test" ]; then 98 | echo "!!!!!! redis install failed !!!!!!" 99 | exit 0 100 | fi 101 | 102 | # install x100speed_transcode python3 require package 103 | sudo /usr/local/bin/pip3 install ${REQUIRE_PYTHON3_MODULE} -i ${PIP3_SOURCE} 104 | python_module_test=$? 105 | if [ "$python_module_test" != "0" ]; then 106 | echo "!!!!!! pip3 install module failed !!!!!!" 107 | fi 108 | 109 | # install x100speed_transcode 110 | cd ${APP_PATH} 111 | 112 | sudo wget ${X100SPEED_TRANSCODE_URL} 113 | sudo tar zxvf ${X100SPEED_TRANSCODE_PACKET} 114 | cd ${X100SPEED_TRANSCODE_NAME} 115 | 116 | sudo \cp ./ci/x100speed_transcode.service /usr/lib/systemd/system 117 | sudo systemctl enable x100speed_transcode 118 | 119 | sudo reboot 120 | -------------------------------------------------------------------------------- /docs/API_Reference.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | API Reference 9 | ============================ 10 | 11 | ### /interface/add\_staff\_ip 12 | #### 描述 13 | 添加staff转码机ip地址 14 | #### 参数 15 | ip : 字符串类型 16 | #### 返回值 17 | json格式返回操作状态以及失败原因 18 | status : success、failed 19 | message : 失败原因 20 | #### 示例 21 | /interface/add_staff_ip?ip=192.168.1.100 22 | 23 | ### /interface/update\_staff\_monitor 24 | #### 描述 25 | 更新staff转码机monitor 26 | #### 参数 27 | ip : staff转码机ip地址 28 | process_count : staff转码机进程数 29 | #### 返回值 30 | json格式返回操作状态以及失败原因 31 | status : success、failed 32 | message : 失败原因 33 | #### 示例 34 | /interface/update_staff_load?ip=192.168.1.100&process_count=1 35 | 36 | ### /interface/get\_video\_id 37 | #### 描述 38 | 生成video id、转码机ip地址 39 | #### 参数 40 | 41 | #### 返回值 42 | json格式返回video id、转码机ip地址 43 | video_id : 视频唯一标识 44 | ip : 转码机ip地址 45 | #### 示例 46 | /interface/get_video_id 47 | 48 | ### /interface/get\_video\_info 49 | #### 描述 50 | 获取视频信息,包括状态、截图总数、转码机ip地址、码率 51 | #### 参数 52 | video_id : 视频唯一标识 53 | #### 返回值 54 | json格式返回视频状态、截图总数、转码机ip、码率 55 | video_status : 视频状态,分别为success、failed、proceed 56 | snap_count : 视频截图总数 57 | ip : 转码机ip地址 58 | bitrates : uuid转码视频码率,支持多码率,用','分隔 59 | #### 示例 60 | /interface/get_video_info?video_id=ytE3V3GyJigi2sqeBK 61 | 62 | ### /interface/update\_video\_status 63 | #### 描述 64 | 设置视频转码状态 65 | #### 参数 66 | video_id : 视频唯一标识 67 | status : 视频转码状态,分别为success、failed、proceed, 设置为proceed时需要加上bitrate参数 68 | bitrate : 转码视频码率, status为proceed时传入此参数,其他状态不需要 69 | #### 返回值 70 | json格式返回操作状态以及失败原因 71 | status : success、failed 72 | message : 失败原因 73 | #### 示例 74 | /interface/update_video_status?video_id=ytwQrjkUBWi0u0syEC&status=success 75 | 76 | ### /interface/get\_video\_multirate\_info 77 | #### 描述 78 | 返回video id及已转码完成的清晰度 79 | #### 参数 80 | count : 获取video_id个数,非必需,默认为1 81 | #### 返回值 82 | json格式返回video id、bitrates 83 | #### 示例 84 | /interface/get_video_multirate_info?video_id=ytwQrjkUBWi0u0syEC&count=5 85 | 86 | ### /interface/delete\_video\_id\_multirate 87 | #### 描述 88 | 删除多清晰度转码队列里video id 89 | #### 参数 90 | video_id : 视频唯一标识 91 | #### 返回值 92 | json格式返回操作状态以及失败原因 93 | status : success、failed 94 | message : 失败原因 95 | #### 示例 96 | /interface/delete_video_id_multirate?video_id=ytwQrjkUBWi0u0syEC 97 | 98 | ### /interface/add\_video\_id\_transcode\_bitrate 99 | #### 描述 100 | 添加已完成的清晰度到video id 101 | #### 参数 102 | video_id : 视频唯一标识 103 | bitrates : 视频码率,添加多个码率用逗号隔开 104 | #### 返回值 105 | json格式返回操作状态以及失败原因 106 | status : success、failed 107 | message : 失败原因 108 | #### 示例 109 | /interface/add_video_id_transcode_bitrate?video_id=ytwQrjkUBWi0u0syEC&bitrates=200,400 110 | 111 | ### /interface/update\_video\_snap\_image\_count 112 | #### 描述 113 | 更新视频截图数量 114 | #### 参数 115 | video_id : 视频唯一标识 116 | snap_image_count : 视频截图总数 117 | #### 返回值 118 | json格式返回操作状态以及失败原因 119 | status : success、failed 120 | message : 失败原因 121 | #### 示例 122 | /interface/update_video_snap_image_count?video_id=ytwQrjkUBWi0u0syEC&snap_image_count=100 123 | 124 | ### /interface/get\_video\_new\_snap\_image 125 | #### 描述 126 | 获取视频最新截图地址 127 | #### 参数 128 | video_id : 视频唯一标识 129 | #### 返回值 130 | json格式返回截图地址以及操作状态、失败原因 131 | status : success、failed 132 | message : 失败原因 133 | image_url : 截图地址 134 | #### 示例 135 | /interface/get_video_new_snap_image?video_id=ytE3V3GyJigi2sqeBK 136 | 137 | ### /interface/add\_video\_segment 138 | #### 描述 139 | 添加切片信息到x100speed_sortedset_videoid_bitrate 140 | #### 参数 141 | video_id : 视频唯一标识 142 | bitrate : 视频码率 143 | fragment_id : 切片序号 144 | hostname : 视频存储主机名 145 | storage_path : 视频存储路径 146 | create_time : 切片文件创建时间 147 | fps : 视频帧率 148 | frame_count : 视频帧数 149 | file_size : 切片文件大小 150 | #### 返回值 151 | json格式返回操作状态以及失败原因 152 | status : success、failed 153 | message : 失败原因 154 | #### 示例 155 | /interface/add_video_segment?video_id=ytwQrjkUBWi0u0syEC&bitrate=200&fragment_id=1&hostname=http://x100speed.com&storage_path=/ZH/CN/ereoimdfmdnndfdkd_200_1.ts&create_time=1349827788&fps=25&frame_count=250&file_size=3430 156 | 157 | ### /interface/\.m3u8 158 | #### 描述 159 | 视频hls播放列表,“”为视频video_id 160 | #### 参数 161 | 162 | #### 返回值 163 | 返回多码率m3u8地址或空 164 | #### 示例 165 | /interface/ytMVnxVFWEocSiWlNe.m3u8 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/Redis_Storage_Design.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | Redis Storage Design 9 | ======================================= 10 | 11 | ### x100speed\_hash\_videoid 12 | #### 描述 13 | Hash数据结构存储视频转码状态、截图数量 14 | key : x100speed_hash_videoid 15 | field : 视频唯一标识video id 16 | value : 以|分割(status|image_count|ip|bitrate),status表示视频转码状态(success、failed、proceed),image_count表示视频截图数量,ip为转码机地址, bitrate为视频码率,多个码率用","隔开 17 | #### 数据结构 18 | | Describe | Data Structure | Key | Field | Value | 19 | | ---------- |:--------------:|:------------------------:|:------------------:|:-------------------------------------------:| 20 | | field name | hash | x100speed\_hash\_videoid | videoid | status\|image_count\|ip\|bitrate1,bitrate2 | 21 | | example | hash | x100speed\_hash\_videoid | ytmaWHUzDikIGwOLl6 | success\|150\|10.209.79.229\|200,400 | 22 | 23 | #### 操作 24 | 更新转码状态、截图数量、ip地址、码率 25 | HSET x100speed_hash_videoid ytmaWHUzDikIGwOLl6 success|150|10.209.79.229|200,400 26 | 27 | ### x100speed\_sortedset\_videoid\_bitrate 28 | #### 描述 29 | Sorted Set数据结构存储视频切片信息,videoid为视频唯一标识,bitrate为转码后的视频码率 30 | key : x100speed_sortedset_videoid_bitrate 31 | score : create_time(切片文件创建时间) 32 | member : 以字符串类型存储fragment_id|hostname|storage_path|create_time|fps|frame_count|file_size 33 | #### 数据结构 34 | | Describe | Data Structure | Key | score | member | 35 | | ---------- |:--------------:|:--------------------------------------:|:-----------:|:--------------------:| 36 | | field name | sorted set | x100speed\_sortedset\_videoid\_bitrate | create_time | fragment\_id\|hostname\|storage\_path\|create\_time\|fps\|frame\_count\|file\_size | 37 | | example | sorted set | x100speed\_sortedset\_videoid\_bitrate | 1437028427 | ytmaWHUzDikIGwOLl6\|http://x100speed.com\|/WH/DK/ytmaWHUzDikIGwOLl6\_1437028427\_cif.ts\|1437028427\|25\|250\|28427| 38 | 39 | #### 操作 40 | 添加切片信息 41 | ZADD x100speed_sortedset_videoid_bitrate 1437028427 ytmaWHUzDikIGwOLl6|http://x100speed.com|/WH/DK/ytmaWHUzDikIGwOLl6_1437028427_cif.ts|1437028427|25|250|28427 42 | 43 | ### x100speed\_hash\_staff 44 | #### 描述 45 | 使用hash结构存储staff转码机ip地址、process_count。ip数据类型为字符串,process_count数据类型整数以字符串形式保存 46 | | Describe | Data Structure | Key | Field | Value | 47 | | ---------- |:--------------:|:----------------------:|:-------------:|:----------------:| 48 | | field name | hash | x100speed\_hash\_staff | ip | process_count | 49 | | example | hash | x100speed\_hash\_staff | 192.168.1.100 | 0 | 50 | 51 | #### 操作 52 | 添加staff主机 53 | HSET x100speed_hash_staff 192.168.1.100 0 54 | 55 | 更新staff主机load 56 | HSET x100speed_hash_staff 192.168.1.100 1 57 | 58 | ### x100speed\_ip\_list 59 | #### 描述 60 | 使用list结构存储转码机(ip)对应待转多个清晰度的videoid 61 | | Describe | Data Structure | Key | Value | 62 | | ---------- |:--------------:|:--------------------------:|:------------------:| 63 | | field name | list | x100speed\_ip\_list | videoid | 64 | | example | list | x100speed\_10.0.2.15\_list | yxzpnmliogGqG2cADk | 65 | 66 | #### 操作 67 | 添加待转多清晰度videoid 68 | RPUSH x100speed_10.0.2.15_list yxzpnmliogGqG2cADk 69 | 70 | -------------------------------------------------------------------------------- /docs/md5_algorithm.md: -------------------------------------------------------------------------------- 1 | # upload_page 2 | 3 | ---------------------- 4 | 5 | ## 描述 6 | 7 | 视频上传页面 8 | 9 | ## 运行server 10 | 11 | python upload.py 12 | 13 | ### 访问 14 | 15 | http://ip/upload.html 16 | 17 | ## uuid md5 计算方式 18 | 19 | echo -n 'ytOb5FJEYgmKU0epYq_2.jpg' | md5sum 20 | 21 | 108fce3dfaf0f3ff6267e8b2d262dba6 22 | 23 | 访问路径分三级目录, 每级目录顺序去md5 3 位: 24 | 25 | http://hostname/108/fce/3df 26 | 27 | -------------------------------------------------------------------------------- /sources/.gitignore: -------------------------------------------------------------------------------- 1 | */__pycache__ 2 | */*/__pycache__ 3 | */*/*/__pycache__ 4 | */*/*/*/__pycache__ 5 | */*/*/*/*/__pycache__ 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | env/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | 63 | -------------------------------------------------------------------------------- /sources/README.md: -------------------------------------------------------------------------------- 1 | # run.py 2 | 3 | 4 | ------------------- 5 | 6 | ## SYNOPSIS 7 | 8 | run server 9 | 10 | ### execute 11 | 12 | python run.py 13 | 14 | ### make daemonize 15 | 16 | python run.py --daemonize 17 | -------------------------------------------------------------------------------- /sources/admin/admin.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask import make_response 3 | import redis 4 | from pprint import pprint 5 | 6 | app = Flask(__name__) 7 | 8 | @app.route('/') 9 | def hello_world(): 10 | return 'Welcome X100speed_transcode Admin System' 11 | 12 | @app.route('/x100speedtranscode/videostaff/add') 13 | @app.route('/x100speedtranscode/videostaff/updata') 14 | def video_staff_add(): 15 | ip = request.args.get('ip') 16 | if(not ip): 17 | return "{'status':'failed', 'message':'ip is null'}" 18 | 19 | load = request.args.get('load') 20 | if(not load): 21 | return "{'status':'failed', 'message':'load is null'}" 22 | 23 | r = redis_connect() 24 | r.hset("video_staff_hash", ip, load) 25 | 26 | return "{'status':'success', 'message':''}" 27 | 28 | @app.route('/x100speedtranscode/videostaff/delete') 29 | def video_staff_delete(): 30 | ip = request.args.get('ip') 31 | if(not ip): 32 | return "{'status':'failed', 'message':'ip is null'}" 33 | 34 | r = redis_connect() 35 | r.hset("video_staff_hash", ip) 36 | 37 | return "{'status':'success', 'message':''}" 38 | 39 | def redis_connect(): 40 | host='127.0.0.1' 41 | port=6379 42 | db=0 43 | password='foobared' 44 | 45 | r = redis.StrictRedis(host = host, port = port, password = password, db = db) 46 | return r 47 | 48 | 49 | if __name__ == '__main__': 50 | app.debug = True 51 | app.run(host='0.0.0.0') 52 | -------------------------------------------------------------------------------- /sources/conf/transcoder.conf: -------------------------------------------------------------------------------- 1 | [base] 2 | hostname=http://10.221.193.64 3 | 4 | [storage] 5 | dir=/tmp/hls/segment 6 | release_dir=/data1/hls/segment 7 | 8 | [segment] 9 | time=10 10 | release_path=/tmp/ts/release 11 | expire=65536 12 | vbitrate=105 13 | abitrate=48 14 | fps=15 15 | fps_count=150 16 | scale=352:288 17 | vcodec=bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0 18 | acodec="libfdk_aac -profile:a aac_he -b:a 16k" 19 | 20 | [snap] 21 | release_path=/tmp/snap/release 22 | fps=0.5 23 | scale=176:144 24 | 25 | [redis] 26 | ip=127.0.0.1 27 | port=6379 28 | 29 | 30 | [config] 31 | file=conf/transcoder.conf 32 | 33 | [url] 34 | add_video_segment=http://10.221.193.64/interface/add_video_segment 35 | update_video_status=http://10.221.193.64/interface/update_video_status 36 | update_video_snap_image_count=http://10.221.193.64/interface/update_video_snap_image_count 37 | 38 | [log] 39 | path=/var/log/x100speed_transcoder.log 40 | 41 | [transcode] 42 | dir=/data1/queue 43 | -------------------------------------------------------------------------------- /sources/interface/tests/x100speed_interface_test.py: -------------------------------------------------------------------------------- 1 | import os, unittest, redis, requests, json 2 | from multiprocessing import Process 3 | from x100speed_interface import * 4 | 5 | class TestSimple(unittest.TestCase): 6 | video_id = "" 7 | staff_ip = "" 8 | 9 | @classmethod 10 | def setUpClass(ConnectionHolder): 11 | os.system("/usr/local/bin/redis-server /etc/redis.conf") 12 | app = X100HTTP() 13 | 14 | app.get("/interface/add_staff_ip", add_staff_ip) 15 | app.get("/interface/update_staff_monitor", update_staff_monitor) 16 | app.get("/interface/get_video_id", get_video_id) 17 | app.get("/interface/update_video_status", update_video_status) 18 | app.get("/interface/get_video_info", get_video_info) 19 | app.get("/interface/update_video_snap_image_count", update_video_snap_image_count) 20 | app.get("/interface/get_video_new_snap_image", get_video_new_snap_image) 21 | app.get("/interface/add_video_segment", add_video_segment) 22 | app.get("/interface/.m3u8", video_play) 23 | app.get("/interface/_.m3u8", video_play_child) 24 | 25 | ConnectionHolder.p = Process(target=app.run, args=('127.0.0.1', 5000)) 26 | ConnectionHolder.p.start() 27 | 28 | @classmethod 29 | def tearDownClass(ConnectionHolder): 30 | os.system("ps auxf |grep redis-server | grep -v grep | awk '{print $2}' | xargs kill") 31 | ConnectionHolder.p.terminate() 32 | 33 | def test_add_staff_ip(self): 34 | payload = {'ip':'127.0.0.1'} 35 | request = requests.get('http://127.0.0.1:5000/interface/add_staff_ip', params=payload) 36 | data = json.loads(request.text) 37 | 38 | self.assertEqual(data['status'], 'success') 39 | 40 | def test_update_staff_monitor(self): 41 | payload = {'ip':'127.0.0.1', 'process_count':'1'} 42 | request = requests.get('http://127.0.0.1:5000/interface/update_staff_monitor', params=payload) 43 | data = json.loads(request.text) 44 | 45 | self.assertEqual(data['status'], 'success') 46 | 47 | def test_get_video_id(self): 48 | payload = {'ip':'127.0.0.1', 'process_count':'0'} 49 | request = requests.get('http://127.0.0.1:5000/interface/update_staff_monitor', params=payload) 50 | data = json.loads(request.text) 51 | 52 | self.assertEqual(data['status'], 'success') 53 | 54 | request = requests.get('http://127.0.0.1:5000/interface/get_video_id') 55 | data = json.loads(request.text) 56 | 57 | self.assertTrue(data['video_id']) 58 | self.assertTrue(data['ip']) 59 | 60 | TestSimple.video_id = data['video_id'] 61 | TestSimple.staff_ip = data['ip'] 62 | 63 | def test_update_video_status(self): 64 | payload = {'video_id': TestSimple.video_id, 'status':'proceed', 'bitrate':'200'} 65 | request = requests.get('http://127.0.0.1:5000/interface/update_video_status', params=payload) 66 | data = json.loads(request.text) 67 | 68 | self.assertEqual(data['status'], 'success') 69 | 70 | def test_get_video_info(self): 71 | payload = {'video_id': TestSimple.video_id} 72 | request = requests.get('http://127.0.0.1:5000/interface/get_video_info', params=payload) 73 | data = json.loads(request.text) 74 | 75 | self.assertTrue(data['status']) 76 | 77 | def test_z_update_video_snap_image_count(self): 78 | payload = {'video_id': TestSimple.video_id, 'snap_image_count':'150'} 79 | request = requests.get('http://127.0.0.1:5000/interface/update_video_snap_image_count', params=payload) 80 | data = json.loads(request.text) 81 | 82 | self.assertEqual(data['status'], 'success') 83 | 84 | def test_z_get_video_new_snap_image(self): 85 | payload = {'video_id': TestSimple.video_id, 'snap_image_count':'150'} 86 | request = requests.get('http://127.0.0.1:5000/interface/update_video_snap_image_count', params=payload) 87 | payload = {'video_id': TestSimple.video_id} 88 | request = requests.get('http://127.0.0.1:5000/interface/get_video_new_snap_image', params=payload) 89 | data = json.loads(request.text) 90 | 91 | self.assertTrue(data['image_url']) 92 | 93 | def test_z_add_video_segment(self): 94 | payload = { 95 | 'video_id': TestSimple.video_id,\ 96 | 'bitrate' :'200',\ 97 | 'fragment_id':'1',\ 98 | 'hostname':'http://127.0.0.1',\ 99 | 'storage_path':'/8ee/2fa/f2e/yuGfhL4RmEIW8EStFU_0.ts',\ 100 | 'create_time':'1437808633',\ 101 | 'fps':'15',\ 102 | 'frame_count':'150',\ 103 | 'file_size':'1105064'\ 104 | } 105 | request = requests.get('http://127.0.0.1:5000/interface/add_video_segment', params=payload) 106 | data = json.loads(request.text) 107 | 108 | self.assertEqual(data['status'], 'success') 109 | 110 | def test_z_video_play(self): 111 | play_url = 'http://127.0.0.1:5000/interface/' + TestSimple.video_id + '.m3u8' 112 | request = requests.get(play_url) 113 | data = request.text 114 | 115 | self.assertTrue(data) 116 | 117 | def test_z_video_play_child(self): 118 | play_url = 'http://127.0.0.1:5000/interface/' + TestSimple.video_id + '_200.m3u8' 119 | request = requests.get(play_url) 120 | data = request.text 121 | 122 | self.assertTrue(data) 123 | 124 | if __name__ == '__main__': 125 | unittest.main() 126 | 127 | -------------------------------------------------------------------------------- /sources/mediainfowrapper/README.md: -------------------------------------------------------------------------------- 1 | # mediainfo wrapper 2 | 3 | 4 | ------------------- 5 | 6 | ## SYNOPSIS 7 | 8 | from mediainfo import MediaInfo 9 | 10 | m = MediaInfo(filename = '/tmp/oooo.flv') 11 | 12 | print(m.video_codec) 13 | 14 | print(m.audio_codec) 15 | 16 | -------------------------------------------------------------------------------- /sources/mediainfowrapper/m.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mediainfo import MediaInfo 4 | 5 | m = MediaInfo(filename = '/tmp/oooo.flv') 6 | 7 | print(m.audio_codec) 8 | -------------------------------------------------------------------------------- /sources/mediainfowrapper/mediainfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import subprocess, re, sys 4 | 5 | class MediaInfo: 6 | def __init__(self, **kwargs): 7 | if not kwargs['filename']: 8 | print("please assgin filename") 9 | sys.exit(1) 10 | 11 | self.filename = kwargs['filename'] 12 | 13 | self.mediainfo() 14 | #self.general() 15 | #self.video() 16 | self.audio() 17 | 18 | def mediainfo(self): 19 | cmd = "mediainfo -f " + self.filename 20 | p = subprocess.check_output(cmd, shell=True) 21 | mediainfo = p.decode() 22 | self.mediainfo = mediainfo 23 | 24 | def general(self): 25 | gen = re.search("(^General\n.*?\n\n)", self.mediainfo, re.S) 26 | if gen is None: 27 | print("media file error") 28 | sys.exit(1) 29 | 30 | gen_info = gen.group(1) 31 | 32 | container_pattern = re.compile("Format\s*:\s*([\w\_\-\\\/\. ]+)\n") 33 | container = self.regex(container_pattern, gen_info) 34 | self.container = re.sub(r"\s+", "", container) 35 | 36 | length_pattern = re.compile("Duration\s*:\s*(\d+)\.?\d*\n") 37 | self.length = self.regex(length_pattern, gen_info) 38 | 39 | bitrate_pattern = re.compile("Overall bit rate\s*:\s*(\d+)\n") 40 | self.bitrate = self.regex(bitrate_pattern, gen_info) 41 | 42 | title_pattern = re.compile("Title\s*:\s*(.+)\n") 43 | self.title = self.regex(title_pattern, gen_info) 44 | 45 | album_pattern = re.compile("Album\s*:\s*(.+)\n") 46 | self.album = self.regex(album_pattern, gen_info) 47 | 48 | track_name_pattern = re.compile("Track name\s*:\s*(.+)\n") 49 | self.track_name = self.regex(track_name_pattern, gen_info) 50 | 51 | performer_pattern = re.compile("Performer\s*:\s*(.+)\n") 52 | self.performer = self.regex(performer_pattern, gen_info) 53 | 54 | def video(self): 55 | video = re.search("(Video[\s\#\d]*\n.*?\n\n)", self.mediainfo, re.S) 56 | if video is None: 57 | return 58 | 59 | video_info = video.group(1) 60 | 61 | video_codec_pattern = re.compile("Codec\s*:\s*([\w\_\-\\\/ ]+)\n") 62 | video_codec = self.regex(video_codec_pattern, video_info) 63 | 64 | video_format_pattern = re.compile("Format\s*:\s*([\w\_\-\\\/ ]+)\n") 65 | video_format = self.regex(video_format_pattern, video_info) 66 | 67 | video_format_profile_pattern = re.compile("Codec profile\s*:\s*([\w\_\-\\\/\@\. ]+)\n") 68 | video_format_profile = self.regex(video_format_profile_pattern, video_info) 69 | 70 | if video_codec: 71 | video_codec = re.sub(r"\s+", "", video_codec) 72 | 73 | if video_format: 74 | video_format = re.sub(r"\s+", "", video_format) 75 | 76 | if video_format_profile: 77 | video_format_profile = re.sub(r"\s+", "", video_format_profile) 78 | 79 | 80 | video_duration_pattern = re.compile("Duration\s*:\s*(\d+)\.?\d*\n") 81 | self.video_duration = self.regex(video_duration_pattern, video_info) 82 | 83 | video_bitrate_pattern = re.compile("Bit rate\s*:\s*(\d+)\n") 84 | self.video_bitrate = self.regex(video_bitrate_pattern, video_info) 85 | 86 | width_pattern = re.compile("Width\s*:\s*(\d+)\n") 87 | self.width = self.regex(width_pattern, video_info) 88 | 89 | height_pattern = re.compile("Height\s*:\s*(\d+)\n") 90 | self.height = self.regex(height_pattern, video_info) 91 | 92 | fps_pattern = re.compile("frame rate\s*:\s*([\d\.]+)\s*fps\n") 93 | self.fps = self.regex(fps_pattern, video_info) 94 | 95 | fps_mode_pattern = re.compile("Frame rate mode\s*:\s*([\w\.]+)\n") 96 | fps_mode = self.regex(fps_mode_pattern, video_info) 97 | 98 | dar_pattern = re.compile("Display aspect ratio\s*:\s*([\d\.]+)\n") 99 | dar = self.regex(dar_pattern, video_info) 100 | 101 | #frame_count = int( fps * video_duration / 1000) 102 | 103 | #if fps and video_duration and frame_count is None: 104 | # fps = str(frame_count / video_duration * 1000)[:6] 105 | 106 | #$frame_count = int($fps * $video_length / 1000) 107 | 108 | # if ( $fps and $video_length and (!$frame_count or $frame_count <= 0)); 109 | #$fps = substr($frame_count / $video_length * 1000, 0, 6) 110 | # if ((!$fps or $fps <= 0) and $video_length and $frame_count); 111 | # $video_length = substr($frame_count / $fps * 1000, 0, 6) 112 | # if ( $fps and (!$video_length or $video_length <= 0) and $frame_count); 113 | # $video_length = $length 114 | # if (!$video_length and $length and $video_info); 115 | # ($rotation) = $video_info =~ /Rotation\s*:\s*([\d\.]+)\n/i; 116 | # $rotation = 0 unless $rotation; 117 | 118 | def audio(self): 119 | audio = re.search("(Audio[\s\#\d]*\n.*?\n\n)", self.mediainfo, re.S) 120 | if audio is None: 121 | return 122 | 123 | audio_info = audio.group(1) 124 | print(audio_info) 125 | 126 | audio_codec_pattern = re.compile("Codec\s*:\s*([\w\_\-\\\/ ]+)\n") 127 | audio_codec = self.regex(audio_codec_pattern, audio_info) 128 | 129 | audio_format_pattern = re.compile("Format\s*:\s*([\w\_\-\\\/ ]+)\n") 130 | audio_format = self.regex(audio_format_pattern, audio_info) 131 | 132 | if audio_codec: 133 | self.audio_codec = re.sub(r"\s+", "", audio_codec) 134 | 135 | if audio_format: 136 | self.audio_format = re.sub(r"\s+", "", audio_format) 137 | 138 | audio_duration_pattern = re.compile("Duration\s*:\s*(\d+)\.?\d*\n") 139 | self.audio_duration = self.regex(audio_duration_pattern, audio_info) 140 | 141 | audio_bitrate_pattern = re.compile("Bit rate\s*:\s*(\d+)\n") 142 | self.audio_bitrate = self.regex(audio_bitrate_pattern, audio_info) 143 | 144 | audio_rate_pattern = re.compile("Sampling rate\s*:\s*(\d+)\n") 145 | self.audio_rate = self.regex(audio_rate_pattern, audio_info) 146 | 147 | audio_language_pattern = re.compile("Language\s*:\s*(\w+)\n") 148 | self.audio_language = self.regex(audio_language_pattern, audio_info) 149 | 150 | 151 | #$audio_length = $video_length 152 | # if ( (!$audio_length or $audio_length <= 0) 153 | # and $video_length 154 | # and $audio_info); 155 | #($audio_language) = $audio_info =~ /Language\s*:\s*(\w+)\n/; 156 | 157 | def regex(self, pattern, string): 158 | result = re.search(pattern, string) 159 | if result is None: 160 | return None 161 | 162 | return result.group(1) 163 | 164 | 165 | -------------------------------------------------------------------------------- /sources/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | from interface.x100speed_interface import * 4 | from transcoder.transcoder import Transcoder 5 | from x100http import X100HTTP, X100Response 6 | from x100daemon import Daemon 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("-d", "--daemonize", help="make daemonize", action="store_true") 10 | args = parser.parse_args() 11 | 12 | if args.daemonize: 13 | pidfile = '/var/run/x100speed_transcoder.pid' 14 | d = Daemon(pidfile) 15 | d.daemonize() 16 | 17 | app = X100HTTP() 18 | app.get("/interface/add_staff_ip", add_staff_ip) 19 | app.get("/interface/update_staff_monitor", update_staff_monitor) 20 | app.get("/interface/get_video_id", get_video_id) 21 | app.get("/interface/update_video_status", update_video_status) 22 | app.get("/interface/get_video_info", get_video_info) 23 | app.get("/interface/get_video_multirate_info", get_video_multirate_info) 24 | app.get("/interface/delete_video_id_multirate", delete_video_id_multirate) 25 | app.get("/interface/add_video_id_transcode_bitrate", add_video_id_transcode_bitrate) 26 | app.get("/interface/update_video_snap_image_count", update_video_snap_image_count) 27 | app.get("/interface/get_video_new_snap_image", get_video_new_snap_image) 28 | app.get("/interface/add_video_segment", add_video_segment) 29 | app.get("/interface/.m3u8", video_play) 30 | app.get("/interface/_.m3u8", video_play_child) 31 | app.upload("/upload", Transcoder) 32 | app.static("/", "/data1/hls/segment/", cors="*") 33 | app.run("0.0.0.0", 80) 34 | -------------------------------------------------------------------------------- /sources/service_report/README.md: -------------------------------------------------------------------------------- 1 | # service_report.py 2 | 3 | 4 | ------------------- 5 | 6 | ## NAME 7 | 8 | Service Report: report local ip to center 9 | 10 | ## SYNOPSIS 11 | 12 | ### execute 13 | 14 | python service_report.py --config=conf/service.conf 15 | 16 | ### make daemonize 17 | 18 | python service_report.py --config=conf/service.conf --daemonize 19 | -------------------------------------------------------------------------------- /sources/service_report/conf/service.conf: -------------------------------------------------------------------------------- 1 | [service_report] 2 | report_ip=10.221.193.64 3 | interval=15 4 | callback=http://10.221.193.64//interface/add_staff_ip 5 | -------------------------------------------------------------------------------- /sources/service_report/service_report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | import argparse 4 | import sys 5 | import socket 6 | import configparser 7 | import os 8 | from x100daemon import Daemon 9 | 10 | 11 | def get_local_ip(): 12 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 13 | try: 14 | # doesn't even have to be reachable 15 | s.connect(('10.255.255.255', 0)) 16 | IP = s.getsockname()[0] 17 | except: 18 | IP = '127.0.0.1' 19 | finally: 20 | s.close() 21 | return IP 22 | 23 | def load_config(configfile): 24 | if not os.path.exists(configfile): 25 | print("no config file") 26 | sys.exit(1) 27 | config = configparser.ConfigParser() 28 | config.read(configfile) 29 | return config 30 | 31 | def main(): 32 | useage = ''' 33 | 34 | ''' 35 | 36 | parser = argparse.ArgumentParser() 37 | parser.add_argument("-d", "--daemonize", help="make daemonize", action="store_true") 38 | parser.add_argument("-f", "--config", help="assign config file path") 39 | 40 | args = parser.parse_args() 41 | 42 | if not args.config: 43 | print("please assign config file path") 44 | sys.exit(1) 45 | 46 | configfile = args.config 47 | config = load_config(configfile) 48 | 49 | interval = int(config['service_report']['interval']) 50 | local_ip = get_local_ip() 51 | 52 | 53 | if args.daemonize: 54 | pidfile = '/var/run/service_report.pid' 55 | d = Daemon(pidfile) 56 | d.daemonize() 57 | 58 | while True: 59 | request_info = 'ip=' + local_ip 60 | 61 | try: 62 | res = http_callback(config['service_report']['callback'], request_info) 63 | except: 64 | print("call update staff monitor interface error") 65 | time.sleep(interval) 66 | continue 67 | 68 | if res['status'] == 'failed': 69 | print('error: %s' % res['message']) 70 | else: 71 | print('success') 72 | sys.exit(0) 73 | 74 | time.sleep(interval) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /sources/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='x100speed_interface', 5 | version='0.0.1', 6 | 7 | description='interface for x100speed', 8 | long_description="", 9 | url='https://github.com/laodifang', 10 | author='Ren Peng', 11 | author_email='ithink.ren@gmail.com', 12 | license='MIT', 13 | 14 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 15 | classifiers=[ 16 | 'Development Status :: 5 - Production/Stable', 17 | 'Intended Audience :: Developers', 18 | 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', 19 | 'Topic :: Multimedia :: Video :: Conversion', 20 | 21 | 'License :: OSI Approved :: MIT License', 22 | 23 | 'Programming Language :: Python :: 3.4', 24 | 'Programming Language :: Python :: 3 :: Only', 25 | ], 26 | keywords='', 27 | 28 | packages=['tests'], 29 | py_modules=['x100speed_interface'], 30 | install_requires = ['requests>=2.7.0'], 31 | test_suite='tests', 32 | ) 33 | 34 | -------------------------------------------------------------------------------- /sources/static/css/x100speed.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #eee; 3 | height: 100%; 4 | text-align: center; 5 | background-color: #38547b; 6 | } 7 | -------------------------------------------------------------------------------- /sources/static/js/x100toolkit.js: -------------------------------------------------------------------------------- 1 | function getUrlParameter(sParam){ 2 | var sPageURL = window.location.search.substring(1); 3 | var sURLVariables = sPageURL.split('&'); 4 | for (var i = 0; i < sURLVariables.length; i++) { 5 | var sParameterName = sURLVariables[i].split('='); 6 | if (sParameterName[0] == sParam) { 7 | return sParameterName[1]; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sources/static/lib/hls/browser.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // requestAnimationFrame polyfill 5 | window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || setTimeout; 6 | 7 | // preconfiguration using 61 | 62 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /sources/static/play_h5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | x100speed html5 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | -------------------------------------------------------------------------------- /sources/static/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 上传视频 6 | 7 | 8 | 9 | 10 | 11 |
12 | 23 |
24 | 25 |
26 |
27 |
28 |

上传视频

29 |
30 | 31 | 32 |
33 | 34 |

35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 | 47 | 48 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 214 | -------------------------------------------------------------------------------- /sources/transcode_origin_file/conf/transcode_origin_file.conf: -------------------------------------------------------------------------------- 1 | [default] 2 | segment_time=10 3 | crf=31 4 | origin_file_path=/data1/queue 5 | get_video_multibitrate_info=http://10.221.193.64/interface/get_video_multirate_info 6 | 7 | vbitrate_SD=280 8 | vbitrate_HD=500 9 | vbitrate_FHD=800 10 | vbitrate_UHD=1800 11 | 12 | abitrate = 48 13 | 14 | scale_SD=640:360 15 | scale_HD=1024:576 16 | scale_FHD=1280:720 17 | scale_UHD=1920:1080 18 | 19 | height_SD=360 20 | height_HD=576 21 | height_FHD=720 22 | height_UHD=1080 23 | 24 | br280_to_type=SD 25 | br500_to_type=HD 26 | br800_to_type=FHD 27 | br1800_to_type=UHD 28 | 29 | 30 | x264opts_lt1100=:scenecut=40:ref=4:bframes=5:b_adapt=2:deblock=0,0:rc_lookahead=50:qcomp=0.52:direct=auto:partitions=all:me=hex:subq=7:trellis=1:psy-rd=1,0.2:chroma_qp_offset=3:aq_mode=3:aq_strength=0.84:keyint=250:min-keyint=100 31 | 32 | x264opts_gt1700=scenecut=40:ref=3:bframes=4:b_adapt=2:deblock=-1,0:rc_lookahead=40:qcomp=0.8:direct=auto:partitions=all:me=hex:subq=7:trellis=2:psy-rd=1.04,0.45:chroma_qp_offset=6:aq_mode=3:aq_strength=1.04:no-fast-pskip:keyint=250:min-keyint=100 33 | 34 | x264opts_btw=:scenecut=40:ref=4:bframes=5:b_adapt=2:deblock=-1,-1:rc_lookahead=50:qcomp=0.6:direct=auto:partitions=all:me=hex:subq=7:trellis=2:psy-rd=1,0.15:chroma_qp_offset=3:aq_mode=3:aq_strength=0.96:keyint=250:min-keyint=100 35 | 36 | x264opts=:scenecut=40:ref=4:bframes=5:b_adapt=2:deblock=-1,-1:rc_lookahead=50:qcomp=0.6:direct=auto:partitions=all:me=hex:subq=7:trellis=2:psy-rd=1,0.15:chroma_qp_offset=3:aq_mode=3:aq_strength=0.96:keyint=250:min-keyint=100 37 | -------------------------------------------------------------------------------- /sources/transcode_origin_file/data/ywOVs9MIA8AUUMSSuI_16.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/sources/transcode_origin_file/data/ywOVs9MIA8AUUMSSuI_16.ts -------------------------------------------------------------------------------- /sources/transcoder/data/test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/sources/transcoder/data/test.ts -------------------------------------------------------------------------------- /sources/transcoder/test/transcoder_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pytnon3 2 | 3 | import unittest 4 | from transcoder import Transcoder 5 | from x100.x100util import * 6 | from x100.x100config import load_config 7 | 8 | class TestStringMethods(unittest.TestCase): 9 | 10 | #transcoder = Transcoder() 11 | def setUp(self): 12 | self.ts_file = 'data/test.ts' 13 | self.flv_file = 'data/test.flv' 14 | 15 | def test_md5(self): 16 | (ts_dir, ts_filename) = self.ts_file.split('/') 17 | expected_md5_string = '9d878e6c8076c7cdd71130118d1fc802' 18 | md5_string = md5(ts_filename) 19 | self.assertEqual(expected_md5_string, md5_string) 20 | 21 | def test_load_conf(self): 22 | config_ob = load_config('conf/transcoder.conf') 23 | self.assertIsNotNone(config_ob) 24 | 25 | def test_request_info_serialize(self): 26 | expected_info = ['a=hello&b=world&', 'b=world&a=hello&'] 27 | info = request_info_serialize(a='hello', b='world') 28 | self.assertIn(info, expected_info) 29 | 30 | def test_get_target_file(self): 31 | config = load_config('conf/transcoder.conf') 32 | release_dir = config['storage']['release_dir'] 33 | (ts_dir, ts_filename) = self.ts_file.split('/') 34 | filetype = 'ts' 35 | (a, b) = get_target_file(release_dir, ts_filename, filetype) 36 | self.assertEqual( get_target_file(release_dir, ts_filename, filetype), 37 | ('/data1/hls/segment/ts/9d8/78e/6c8/test.ts', '/ts/9d8/78e/6c8/test.ts')) 38 | 39 | def test_build_cmd(self): 40 | expected_ffmpeg_cmd = 'ffmpeg -v verbose -i - -filter_complex " [0:v:0]fps=15,scale=352:288,split=2[voutA][vtmpB], [vtmpB]fps=0.5,scale=176:144[voutB],[0:a:0]asplit=1[aoutA]" -map [voutA] -map [aoutA] -c:v libx264 -x264opts bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0 -c:a libfdk_aac -profile:a aac_he -b:a 16k-f segment -segment_format flv -segment_time 10 -y /tmp/hls/segment/abc_%d.flv -map [voutB] -y /tmp/hls/segment/abc_%d.jpg 2>&1' 41 | ffmpeg_cmd = build_cmd('abc') 42 | self.assertEqual(expected_ffmpeg_cmd, ffmpeg_cmd) 43 | 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | 48 | 49 | -------------------------------------------------------------------------------- /sources/transcoder/transcoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, sys, re, select, subprocess, io, time, shutil, logging 3 | import urllib.request 4 | import x100mpegts 5 | from x100utils.x100config import load_config 6 | from x100utils.x100util import * 7 | from x100utils.x100request import http_callback, update_video_status 8 | 9 | class TranscoderLogger: 10 | def __init__(self, logfile): 11 | self.logger = logging.getLogger() 12 | self.logger.setLevel(logging.INFO) 13 | 14 | handler = logging.FileHandler(logfile) 15 | handler.setLevel(logging.INFO) 16 | 17 | formatter = logging.Formatter('[%(levelname)s] %(asctime)s %(name)s %(message)s') 18 | handler.setFormatter(formatter) 19 | 20 | self.logger.addHandler(handler) 21 | 22 | 23 | class Transcoder: 24 | def __init__(self): 25 | self.config = load_config('conf/transcoder.conf') 26 | self.bitrate = int(self.config['segment']['vbitrate']) + int(self.config['segment']['abitrate']) 27 | self.logger = TranscoderLogger(self.config['log']['path']).logger 28 | self.video_id = '' 29 | 30 | def upload_start(self, req): 31 | print("hello") 32 | 33 | def upload_process(self, key, line): 34 | if key == b'video_id': 35 | video_id = line.decode().rstrip() 36 | self.video_id = video_id 37 | 38 | self.init_popen_handler() 39 | 40 | request_info = request_info_serialize(video_id=self.video_id, status='proceed', bitrate=str(self.bitrate)) 41 | res = http_callback(self.config['url']['update_video_status'], request_info) 42 | self.log(res, self.video_id, 'update_video_status', None) 43 | 44 | elif key == b'upload': 45 | self.run_cmd_async(line) 46 | self.write_original_file(line) 47 | 48 | def upload_finish(self,req): 49 | return "your file uploaded." 50 | 51 | def init_popen_handler(self): 52 | cmd = build_cmd(self.video_id, self.config) 53 | print(cmd) 54 | self.logger.info("ffmpeg_cmd: %s" % cmd) 55 | self.open_original_file_handler() 56 | p = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 57 | self.stdout = p.stdout 58 | self.stdin = p.stdin 59 | self.poll = p.poll() 60 | self.stdout = non_blocking_handler(self.stdout) 61 | 62 | def run_cmd_async(self, body): 63 | try: 64 | self.stdin.write(body) 65 | except: 66 | request_info = request_info_serialize(video_id=self.video_id, status='failed', bitrate=str(self.bitrate)) 67 | res = http_callback(self.config['url']['update_video_status'], request_info) 68 | self.log(res, self.video_id, 'update_video_status', 'stdin.write error line 61') 69 | return 1 70 | 71 | running = self.running() 72 | while running: 73 | line = self.stdout.read(-1) 74 | #segment:'/tmp/a_sd_000030.flv' count:30 endedp=24 drop=0 75 | if line is None: 76 | break 77 | line = line.decode() 78 | ts_re = re.search("segment:\'(.*?)\'\s+count:(\d+).*", line) 79 | if ts_re: 80 | ts_file = ts_re.group(1) 81 | ts_file_index = ts_re.group(2) 82 | ts_filename = ts_file.split('/')[-1] 83 | 84 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], ts_filename, 'ts') 85 | 86 | retcode = flv2ts(ts_file, target_file) 87 | if retcode != 0: 88 | self.logger.error("flv2ts flvfile: %s tsfile: %s failed", ts_file, target_file) 89 | continue 90 | 91 | request_info = self.segment_request_info(target_file, storage_path, ts_file_index) 92 | add_video_segment_url = self.config['url']['add_video_segment'] 93 | 94 | res = http_callback( add_video_segment_url, request_info) 95 | self.log(res, self.video_id, 'add_video_segment', storage_path) 96 | 97 | snap_re = re.search("snap:\'(.*?)\'\s+count:(\d+).*", line) 98 | if snap_re: 99 | snap_img_file = snap_re.group(1) 100 | snap_index = snap_re.group(2) 101 | snap_filename = snap_img_file.split('/')[-1] 102 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], snap_filename, 'snap') 103 | 104 | shutil.move(snap_img_file, target_file) 105 | 106 | info = request_info_serialize(video_id=self.video_id, snap_image_count=snap_index) 107 | 108 | res = http_callback(self.config['url']['update_video_snap_image_count'], info) 109 | self.log(res, self.video_id, 'update_video_snap_image_count', storage_path) 110 | 111 | def segment_request_info(self, filepath, storage_path, file_index): 112 | info = x100mpegts.info(filepath) 113 | create_time = info['mtime'] 114 | file_size = info['file_size'] 115 | bitrate = info['bitrate'] 116 | frame_count = info['frame_count'] 117 | fps = info['fps'] 118 | 119 | video_id = self.video_id 120 | hostname = self.config['base']['hostname'] 121 | storage_path = storage_path 122 | fragment_id = file_index 123 | 124 | req_info = request_info_serialize( 125 | video_id=video_id, hostname=hostname,\ 126 | storage_path=storage_path, frame_count=frame_count,\ 127 | file_size=file_size, fragment_id=file_index, bitrate=bitrate,\ 128 | fps=fps, create_time=create_time) 129 | return req_info 130 | 131 | def running(self): 132 | return self.poll is None 133 | 134 | def log(self, response, video_id, apiname, filename): 135 | if response['status'] == 'success': 136 | self.logger.info("[video_id] %s [snap] %s [callbackApi] %s success", video_id, filename, apiname) 137 | else: 138 | self.logger.error("[video_id] %s [snap] %s [callbackApi] %s error: %s", video_id, filename, apiname, response['message']) 139 | return 140 | 141 | def __del__(self): 142 | request_info = request_info_serialize(video_id=self.video_id, status='success', bitrate=str(self.bitrate)) 143 | res = http_callback(self.config['url']['update_video_status'], request_info) 144 | self.log(res, self.video_id, 'update_video_status', None) 145 | 146 | def open_original_file_handler(self): 147 | filename = self.config['transcode']['dir'] + '/' + self.video_id 148 | self.original_file_handler = open(filename, 'wb+') 149 | 150 | def write_original_file(self, line): 151 | self.original_file_handler.write(line) 152 | -------------------------------------------------------------------------------- /sources/transcoder/transcoder_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pytnon3 2 | 3 | import unittest 4 | from unittest import TestCase 5 | import sys 6 | sys.path.append("../") 7 | from transcoder import Transcoder 8 | from x100utils.x100util import * 9 | from x100utils.x100config import load_config 10 | 11 | class TestMethods(unittest.TestCase): 12 | 13 | #transcoder = Transcoder() 14 | def setUp(self): 15 | self.ts_file = 'data/test.ts' 16 | self.flv_file = 'data/test.flv' 17 | self.maxDiff = None 18 | 19 | def test_md5(self): 20 | (ts_dir, ts_filename) = self.ts_file.split('/') 21 | expected_md5_string = '9d878e6c8076c7cdd71130118d1fc802' 22 | md5_string = md5(ts_filename) 23 | self.assertEqual(expected_md5_string, md5_string) 24 | 25 | def test_load_conf(self): 26 | config_ob = load_config('../conf/transcoder.conf') 27 | self.assertIsNotNone(config_ob) 28 | 29 | def test_request_info_serialize(self): 30 | expected_info = ['a=hello&b=world&', 'b=world&a=hello&'] 31 | info = request_info_serialize(a='hello', b='world') 32 | self.assertIn(info, expected_info) 33 | 34 | def test_get_target_file(self): 35 | config = load_config('../conf/transcoder.conf') 36 | release_dir = config['storage']['release_dir'] 37 | (ts_dir, ts_filename) = self.ts_file.split('/') 38 | filetype = 'ts' 39 | (a, b) = get_target_file(release_dir, ts_filename, filetype) 40 | self.assertEqual( get_target_file(release_dir, ts_filename, filetype), 41 | ('/data1/hls/segment/ts/9d8/78e/6c8/test.ts', '/ts/9d8/78e/6c8/test.ts')) 42 | 43 | #def test_build_cmd(self): 44 | # config = load_config('../conf/transcoder.conf') 45 | # expected_ffmpeg_cmd = 'ffmpeg -v verbose -i - -filter_complex " [0:v:0]fps=15,scale=352:288,split=2[voutA][vtmpB], [vtmpB]fps=0.5,scale=176:144[voutB],[0:a:0]asplit=1[aoutA]" -map [voutA] -map [aoutA] -c:v libx264 -x264opts bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0 -c:a libfdk_aac -profile:a aac_he -b:a 16k-f segment -segment_format flv -segment_time 10 -y /tmp/hls/segment/abc_%d.flv -map [voutB] -y /tmp/hls/segment/abc_%d.jpg 2>&1' 46 | # ffmpeg_cmd = build_cmd('abc', config) 47 | # self.assertEqual(expected_ffmpeg_cmd, ffmpeg_cmd, 'why not equal') 48 | 49 | 50 | if __name__ == "__main__": 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /sources/x100monitor/README.md: -------------------------------------------------------------------------------- 1 | # x100monitor 2 | 3 | 4 | ------------------- 5 | 6 | ## NAME 7 | 8 | Monitor Client 9 | 10 | ## SYNOPSIS 11 | 12 | ### execute 13 | 14 | python monitor_client.py --config=conf/monitor.conf 15 | 16 | ### make daemonize 17 | 18 | python monitor_client.py --config=conf/monitor.conf --daemonize 19 | -------------------------------------------------------------------------------- /sources/x100monitor/conf/monitor.conf: -------------------------------------------------------------------------------- 1 | [monitor] 2 | want_process="ffmpeg" 3 | interval=15 4 | callback=http://10.221.193.64/interface/update_staff_monitor 5 | -------------------------------------------------------------------------------- /sources/x100monitor/monitor_client.py: -------------------------------------------------------------------------------- 1 | import argparse, socket, sys, os, configparser, urllib, time 2 | from x100daemon import Daemon 3 | from x100monitor import x100Monitor 4 | 5 | def load_config(configfile): 6 | if not os.path.exists(configfile): 7 | print("no config file") 8 | sys.exit(1) 9 | config = configparser.ConfigParser() 10 | config.read(configfile) 11 | return config 12 | 13 | def get_local_ip(): 14 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 15 | try: 16 | # doesn't even have to be reachable 17 | s.connect(('10.255.255.255', 0)) 18 | IP = s.getsockname()[0] 19 | except: 20 | IP = '127.0.0.1' 21 | finally: 22 | s.close() 23 | return IP 24 | 25 | def http_callback(url, info): 26 | request_url = url + '?' + info 27 | request = urllib.request.Request(request_url) 28 | with urllib.request.urlopen(request) as f: 29 | return json.loads(f.read().decode('utf-8')) 30 | 31 | def main(): 32 | useage = ''' 33 | 34 | ''' 35 | 36 | parser = argparse.ArgumentParser() 37 | parser.add_argument("-d", "--daemonize", help="make daemonize", action="store_true") 38 | parser.add_argument("-f", "--config", help="assign config file path") 39 | 40 | args = parser.parse_args() 41 | 42 | if not args.config: 43 | print("please assign config file path") 44 | sys.exit(1) 45 | 46 | configfile = args.config 47 | config = load_config(configfile) 48 | 49 | interval = int(config['monitor']['interval']) 50 | want_process = config['monitor']['want_process'].rstrip('"') 51 | local_ip = get_local_ip() 52 | 53 | m = x100Monitor() 54 | 55 | if args.daemonize: 56 | pidfile = '/var/run/monitor_client.pid' 57 | d = Daemon(pidfile) 58 | d.daemonize() 59 | 60 | while True: 61 | process_info = m.process(want_process) 62 | process_count = len(process_info) 63 | 64 | request_info = 'ip=' + local_ip + '&process_count=' + str(process_count) 65 | try: 66 | res = http_callback(config['monitor']['callback'], request_info) 67 | except: 68 | print("call update staff monitor interface error") 69 | time.sleep(interval) 70 | continue 71 | 72 | if res['status'] == 'failed': 73 | print('error: %s' % res['message']) 74 | else: 75 | print('success') 76 | 77 | time.sleep(interval) 78 | 79 | 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /sources/x100monitor/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='x100monitor', 5 | version='0.1.0', 6 | 7 | description='get system basic infomation', 8 | long_description=open('README.rst').read(), 9 | #url='https://github.com/WayneZhouChina/x100monitor', 10 | author='Wayne Zhou', 11 | author_email='cumtxhzyy@gmail.com', 12 | license='MIT', 13 | 14 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 15 | classifiers=[ 16 | 'Development Status :: 4 - Beta', 17 | 'Intended Audience :: Developers', 18 | 19 | 'License :: OSI Approved :: MIT License', 20 | 21 | 'Programming Language :: Python :: 3.3', 22 | 'Programming Language :: Python :: 3.4', 23 | 'Programming Language :: Python :: 3 :: Only', 24 | ], 25 | keywords='x100 monitor system', 26 | 27 | py_modules=['x100monitor'], 28 | test_suite='test', 29 | ) 30 | -------------------------------------------------------------------------------- /sources/x100monitor/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | class TestStringMethods(unittest.TestCase): 4 | pass 5 | 6 | if __name__ == "__main__": 7 | unittest.main() 8 | -------------------------------------------------------------------------------- /sources/x100monitor/x100monitor.py: -------------------------------------------------------------------------------- 1 | import subprocess, re 2 | import socket 3 | import urllib.request 4 | import json 5 | 6 | class x100Monitor: 7 | 8 | def get_uptime(self): 9 | 10 | with open('/proc/uptime', 'r') as line: 11 | contents = line.read().split() 12 | 13 | total_seconds = float(contents[0]) 14 | 15 | MINUTE = 60 16 | HOUR = MINUTE * 60 17 | DAY = HOUR * 24 18 | 19 | days = int( total_seconds / DAY ) 20 | hours = int( ( total_seconds % DAY ) / HOUR ) 21 | minutes = int( ( total_seconds % HOUR ) / MINUTE ) 22 | seconds = int( total_seconds % MINUTE ) 23 | 24 | uptime = "{0} days {1} hours {2} minutes {3} seconds".format(days, hours, minutes, seconds) 25 | 26 | return uptime 27 | 28 | def process(self, find_cmd=None): 29 | stats = subprocess.Popen(['pidstat','-ruht'], stdout=subprocess.PIPE, close_fds=True).communicate()[0] 30 | 31 | stats_data = stats.splitlines() 32 | del stats_data[0:2] # Deletes system data 33 | 34 | converted_data = [] 35 | for line in stats_data: 36 | if re.search(b'command', line, re.IGNORECASE): 37 | header = line.decode().split() 38 | del header[0] 39 | else: 40 | command = line.decode().split() 41 | cmd = command[-1] 42 | if find_cmd is not None: 43 | if cmd not in find_cmd: 44 | continue 45 | data_dict = dict(zip(header, command)) 46 | 47 | process_memory_mb = float(1000) * float(data_dict["%MEM"].replace(',', '.')) 48 | memory = "{0:.3}".format(process_memory_mb) 49 | memory = memory.replace(",", ".") 50 | 51 | cpu = "{0:.2f}".format( float( data_dict["%CPU"].replace(",", ".") ) ) 52 | cpu = cpu.replace(",", ".") 53 | 54 | command = data_dict["Command"] 55 | if not re.search("_", command, re.IGNORECASE): 56 | extracted_data = { "cpu:%": cpu, 57 | "memory:mb": memory, 58 | "command:" : command} 59 | converted_data.append(extracted_data) 60 | return converted_data 61 | 62 | def get_load_average(self): 63 | _loadavg_columns = ['minute','five_minutes','fifteen_minutes','scheduled_processes'] 64 | 65 | 66 | lines = open('/proc/loadavg','r').readlines() 67 | 68 | load_data = lines[0].split() 69 | 70 | _loadavg_values = load_data[:4] 71 | 72 | load_dict = dict(zip(_loadavg_columns, _loadavg_values)) 73 | 74 | 75 | # Get cpu cores 76 | cpuinfo = subprocess.Popen(['cat', '/proc/cpuinfo'], stdout=subprocess.PIPE, close_fds=True) 77 | grep = subprocess.Popen(['grep', 'cores'], stdin=cpuinfo.stdout, stdout=subprocess.PIPE, close_fds=True) 78 | sort = subprocess.Popen(['sort', '-u'], stdin=grep.stdout, stdout=subprocess.PIPE, close_fds=True)\ 79 | .communicate()[0] 80 | 81 | cores = re.findall(r'\d+', sort.decode()) 82 | 83 | try: 84 | load_dict['cores'] = int(cores[0]) 85 | except: 86 | load_dict['cores'] = 1 # Don't break if can't detect the cores 87 | 88 | return load_dict 89 | 90 | def get_memory_info(self): 91 | 92 | memory_dict = {} 93 | _save_to_dict = ['MemFree', 'MemTotal', 'SwapFree', 'SwapTotal', 'Buffers', 'Cached'] 94 | 95 | regex = re.compile(r'([0-9]+)') 96 | 97 | with open('/proc/meminfo', 'r') as lines: 98 | 99 | for line in lines: 100 | values = line.split(':') 101 | 102 | match = re.search(regex, values[1]) 103 | if values[0] in _save_to_dict: 104 | memory_dict[values[0].lower()] = int(match.group(0)) / 1024 # Convert to MB 105 | 106 | # Unix releases buffers and cached when needed 107 | buffers = memory_dict.get('buffers', 0) 108 | cached = memory_dict.get('cached', 0) 109 | 110 | memory_free = memory_dict['memfree']+buffers+cached 111 | memory_used = memory_dict['memtotal']-memory_free 112 | memory_percent_used = (float(memory_used)/float(memory_dict['memtotal'])*100) 113 | 114 | swap_total = memory_dict.get('swaptotal', 0) 115 | swap_free = memory_dict.get('swapfree', 0) 116 | swap_used = swap_total-swap_free 117 | swap_percent_used = 0 118 | 119 | if swap_total > 0: 120 | swap_percent_used = (float(swap_used)/float(swap_total) * 100) 121 | 122 | extracted_data = { 123 | "memory:total:mb": memory_dict["memtotal"], 124 | "memory:free:mb": memory_free, 125 | "memory:used:mb": memory_used, 126 | "memory:used:%": memory_percent_used, 127 | "swap:total:mb":swap_total, 128 | "swap:free:mb": swap_free, 129 | "swap:used:mb": swap_used, 130 | "swap:used:%": swap_percent_used 131 | } 132 | 133 | # Convert everything to int to avoid float localization problems 134 | for k,v in extracted_data.items(): 135 | extracted_data[k] = int(v) 136 | 137 | return extracted_data 138 | 139 | def get_disk_usage(self): 140 | df = subprocess.Popen(['df','-h'], stdout=subprocess.PIPE, close_fds=True).communicate()[0] 141 | 142 | volumes = df.decode().split('\n') 143 | volumes.pop(0) # remove the header 144 | volumes.pop() 145 | 146 | data = {} 147 | 148 | _columns = ('volume', 'total', 'used', 'free', 'percent', 'path') 149 | 150 | previous_line = None 151 | 152 | for volume in volumes: 153 | line = volume.split(None, 6) 154 | 155 | if len(line) == 1: # If the length is 1 then this just has the mount name 156 | previous_line = line[0] # We store it, then continue the for 157 | continue 158 | 159 | if previous_line != None: 160 | line.insert(0, previous_line) # then we need to insert it into the volume 161 | previous_line = None # reset the line 162 | 163 | if line[0].startswith('/'): 164 | _volume = dict(zip(_columns, line)) 165 | 166 | _volume['percent'] = _volume['percent'].replace("%",'') # Delete the % sign for easier calculation later 167 | 168 | # strip /dev/ 169 | _name = _volume['volume'].replace('/dev/', '') 170 | 171 | # Encrypted directories -> /home/something/.Private 172 | if '.' in _name: 173 | _name = _name.replace('.','') 174 | 175 | data[_name] = _volume 176 | 177 | return data 178 | -------------------------------------------------------------------------------- /sources/x100utils/x100config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, configparser, sys 3 | 4 | def load_config(configfile): 5 | #configfile = 'conf/transcoder.conf' 6 | if not os.path.exists(configfile): 7 | print("no config file") 8 | sys.exit(1) 9 | config = configparser.ConfigParser() 10 | config.read(configfile) 11 | return config 12 | -------------------------------------------------------------------------------- /sources/x100utils/x100http.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib 3 | import json 4 | from x100.x100util import create_request_info 5 | 6 | def http_callback(url, info): 7 | request_url = url + '?' + info 8 | request = urllib.request.Request(request_url) 9 | with urllib.request.urlopen(request) as f: 10 | return json.loads(f.read().decode('utf-8')) 11 | 12 | def video_bitrate_add(url, uuid, bitrate): 13 | #url = 'http://10.221.193.196:5000/interface/video_uuid_bitrate_add' 14 | #http://10.221.193.196:5000/interface/video_uuid_bitrate_add?uuid=ytE3V3GyJigi2sqeBK&bitrate=20 15 | info = 'bitrate=' + bitrate + '&uuid=' + uuid 16 | return http_callback(url, info) 17 | 18 | def update_video_status(url, video_id, status, bitrate=None): 19 | if bitrate is not None: 20 | info = create_request_info(video_id=video_id, bitrate=bitrate, status=status) 21 | else: 22 | info = create_request_info(video_id=video_id, status=status) 23 | print("xxxxxxxxxxxxxxxxxxxxxx") 24 | print(url) 25 | print(info) 26 | print("xxxxxxxxxxxxxxxxxxxxxx") 27 | return http_callback(url, info) 28 | -------------------------------------------------------------------------------- /sources/x100utils/x100redis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/evn python 2 | import x100redis 3 | 4 | def redis_conn(self): 5 | r = redis.StrictRedis(host=self.config['redis']['ip'], port=self.config['redis']['port'], db=0) 6 | return r 7 | 8 | def insert_redis(self, score, member): 9 | r = self.redis_conn() 10 | zz_key = self.segment_list_name 11 | print(self.segment_list_name) 12 | print(member) 13 | r.zadd(zz_key, score, member) 14 | r.expire(zz_key, self.config['segment']['expire']) 15 | info("insert redis ok") 16 | return 1 17 | 18 | def remove_redis(self, zz_key, member): 19 | r = self.redis_conn() 20 | zz_key = self.segment_list_name 21 | r.zrem(zz_key, member) 22 | info("delete redis ok") 23 | return 1 24 | 25 | -------------------------------------------------------------------------------- /sources/x100utils/x100request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib 3 | import json 4 | from x100utils.x100util import request_info_serialize 5 | 6 | def http_callback(url, info): 7 | request_url = url + '?' + info 8 | request = urllib.request.Request(request_url) 9 | with urllib.request.urlopen(request) as f: 10 | return json.loads(f.read().decode('utf-8')) 11 | 12 | 13 | def video_bitrate_add(url, uuid, bitrate): 14 | #url = 'http://10.221.193.196:5000/interface/video_uuid_bitrate_add' 15 | #http://10.221.193.196:5000/interface/video_uuid_bitrate_add?uuid=ytE3V3GyJigi2sqeBK&bitrate=20 16 | info = 'bitrate=' + bitrate + '&uuid=' + uuid 17 | return http_callback(url, info) 18 | 19 | def update_video_status(url, video_id, status, bitrate=None): 20 | if bitrate is not None: 21 | info = request_info_serialize(video_id=video_id, bitrate=bitrate, status=status) 22 | else: 23 | info = request_info_serialize(video_id=video_id, status=status) 24 | return http_callback(url, info) 25 | -------------------------------------------------------------------------------- /sources/x100utils/x100util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import hashlib, os, sys, subprocess 3 | from fcntl import fcntl, F_GETFL, F_SETFL 4 | from os import O_NONBLOCK 5 | from x100utils.x100config import load_config 6 | 7 | def md5(filename): 8 | m = hashlib.md5() 9 | m.update(filename.encode()) 10 | md5_str = m.hexdigest() 11 | return md5_str 12 | 13 | def file_create_time(afile): 14 | if not os.path.exists(afile): 15 | print("file %s not exist" % afile) 16 | sys.exit(1) 17 | return str(int(os.path.getctime(afile))) 18 | 19 | def file_size(afile): 20 | if not os.path.exists(afile): 21 | print("file %s not exist" % afile) 22 | sys.exit(1) 23 | statinfo = os.stat(afile) 24 | return statinfo.st_size 25 | 26 | def non_blocking_handler(handler): 27 | flags = fcntl(handler, F_GETFL) 28 | fcntl(handler, F_SETFL, flags | O_NONBLOCK) 29 | return handler 30 | 31 | def request_info_serialize(**kwargs): 32 | info = "" 33 | for k, v in kwargs.items(): 34 | info += str(k) + '=' + str(v) + '&' 35 | return info 36 | 37 | def get_target_file(release_dir, filename, file_type): 38 | 39 | md5_str = md5(filename) 40 | 41 | dir1 = md5_str[:3] 42 | dir2 = md5_str[3:6] 43 | dir3 = md5_str[6:9] 44 | 45 | target_dir = release_dir + '/' + file_type + '/' + dir1 + '/' + dir2 + '/' + dir3 46 | if not os.path.exists(target_dir): 47 | os.makedirs(target_dir) 48 | 49 | target_filename = target_dir + '/' + filename 50 | storage_path = '/'+ file_type +'/'+ dir1 +'/'+ dir2 +'/'+ dir3 +'/'+ filename 51 | 52 | target_filename = target_filename.replace('.flv', '.ts') 53 | storage_path = storage_path.replace('.flv', '.ts') 54 | return (target_filename, storage_path) 55 | 56 | def flv2ts(flvfile, tsfile): 57 | flv2ts_cmd = cmd = "ffmpeg -i " + flvfile +" -c copy -bsf:v h264_mp4toannexb -y "+ tsfile +" &> /dev/null" 58 | retcode = subprocess.check_call(flv2ts_cmd, shell=True) 59 | os.remove(flvfile) 60 | return retcode 61 | 62 | def build_cmd(video_id): 63 | config = load_config('conf/transcoder.conf') 64 | storage_dir = config['storage']['dir'] 65 | if not os.path.exists(storage_dir): 66 | os.makedirs(storage_dir) 67 | 68 | tmp_ts_name = storage_dir + '/' + video_id + "_%d.flv" 69 | tmp_snap_name = storage_dir + '/' + video_id + "_%d.jpg" 70 | vbitrate = config['segment']['vbitrate'] 71 | abitrate = config['segment']['abitrate'] 72 | segment_time = config['segment']['time'] 73 | fps = config['segment']['fps'] 74 | scale = config['segment']['scale'] 75 | vcodec = config['segment']['vcodec'] 76 | acodec = config['segment']['acodec'].strip('"') 77 | img_fps = config['snap']['fps'] 78 | img_scale = config['snap']['scale'] 79 | 80 | cmd = "" 81 | cmd += "ffmpeg -v verbose -i -" 82 | cmd += " -filter_complex \"" 83 | cmd += " [0:v:0]fps=" + fps + ",scale=" + scale + ",split=2[voutA][vtmpB]," 84 | cmd += " [vtmpB]fps=" + img_fps + ",scale=" + img_scale + "[voutB],[0:a:0]asplit=1[aoutA]" 85 | cmd += "\" " 86 | cmd += " -map [voutA] -map [aoutA] -c:v libx264 -x264opts " + vcodec 87 | cmd += " -c:a " + acodec + " -f segment -segment_format flv -segment_time " + segment_time 88 | cmd += " -y "+ tmp_ts_name +" -map [voutB] -y " + tmp_snap_name + " 2>&1" 89 | 90 | if cmd is not None: 91 | cmd = cmd 92 | else: 93 | cmd = "" 94 | 95 | return cmd 96 | -------------------------------------------------------------------------------- /tools/burndown_drawer.pl: -------------------------------------------------------------------------------- 1 | use GD::Graph::lines; 2 | use utf8; 3 | 4 | my @data = ( 5 | ["start", "8.5", "8.6", "8.7", "8.8", "8.10", "8.11", "8.12", "8.13", "8.14", "8.15", "8.17", "8.18"], 6 | [ 199, 179, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 7 | [ 199, 182.5, 166, 149.5, 133, 116.5, 100, 83.5, 67, 50.5, 34, 17.5, 0], 8 | ); 9 | 10 | my $graph = GD::Graph::lines->new(800, 600); 11 | 12 | $graph->set( 13 | dclrs => [ qw(black cyan) ], 14 | line_width => 6, 15 | x_label => 'Date', 16 | x_label_position => 1/2, 17 | y_label => 'Workload(Man-Hour)', 18 | y_number_format => sub { return int(shift); }, 19 | title => 'Fourth Round (8.5~8.18) Burndown', 20 | y_max_value => 199, 21 | y_tick_number => 199/12, 22 | y_label_skip => 1, 23 | ) or die $graph->error; 24 | 25 | my $gd = $graph->plot(\@data) or die $graph->error; 26 | open(IMG, '>file.png') or die $!; 27 | binmode IMG; 28 | print IMG $gd->png; 29 | -------------------------------------------------------------------------------- /tools/fake_m3u8.py: -------------------------------------------------------------------------------- 1 | from flask import Flask,make_response 2 | import time 3 | app = Flask(__name__) 4 | 5 | segment_time = 10 6 | segment_count = 3 7 | 8 | def get_vod_segment_list(): 9 | return [1, 2, 3] 10 | 11 | def get_live_segment_list(): 12 | ts = int(time.time()) 13 | mod = ts % (segment_time * segment_count) 14 | segment_list = [] 15 | if mod >= 0 and mod <= segment_time: 16 | segment_list = [1, 2, 3] 17 | elif mod >= segment_time and mod <= segment_time * 2: 18 | segment_list = [2, 3, 1] 19 | elif mod >= segment_time * 2 and mod <= segment_time * 3: 20 | segment_list = [3, 1, 2] 21 | return segment_list 22 | 23 | def get_m3u8_start_vod(): 24 | m3u8_start = "#EXT-X-VERSION:3\n" 25 | m3u8_start += "#EXT-X-MEDIA-SEQUENCE:0\n" 26 | m3u8_start += "#EXT-X-TARGETDURATION:" + str(segment_time+1) + "\n" 27 | return m3u8_start 28 | 29 | def get_m3u8_end_vod(): 30 | return "#EXT-X-ENDLIST\n" 31 | 32 | def get_m3u8_start_live(): 33 | ts = int(time.time()) 34 | mod = ts % 100000 35 | sequence = int( ( ts % 100000 ) / 10 ) 36 | 37 | m3u8_start = "#EXT-X-VERSION:3\n" 38 | m3u8_start += "#EXT-X-MEDIA-SEQUENCE:" + str(sequence) + "\n" 39 | m3u8_start += "#EXT-X-TARGETDURATION:" + str(segment_time+1) + "\n" 40 | return m3u8_start 41 | 42 | @app.route('/vod.m3u8') 43 | def vod_m3u8(): 44 | response = make_response() 45 | response.headers['Content-Type'] = 'application/vnd.apple.mpegurl' 46 | response.headers['Access-Control-Allow-Methods'] = 'GET' 47 | response.headers['Access-Control-Allow-Origin'] = '*' 48 | 49 | result = get_m3u8_start_vod() 50 | for segment_numer in get_vod_segment_list(): 51 | result += "#EXTINF:" + str(segment_time) 52 | result += "\n" 53 | result += "2015070021_cif_00000" + str(segment_numer) + ".ts" 54 | result += "\n" 55 | result += get_m3u8_end_vod() 56 | response.data = result 57 | return response 58 | 59 | @app.route('/live.m3u8') 60 | def live_m3u8(): 61 | response = make_response() 62 | response.headers['Content-Type'] = 'application/vnd.apple.mpegurl' 63 | response.headers['Access-Control-Allow-Methods'] = 'GET' 64 | response.headers['Access-Control-Allow-Origin'] = '*' 65 | 66 | result = get_m3u8_start_live() 67 | for segment_numer in get_live_segment_list(): 68 | result += "#EXTINF:" + str(segment_time) 69 | result += "\n" 70 | result += "2015070021_cif_00000" + str(segment_numer) + ".ts" 71 | result += "\n" 72 | response.data = result 73 | return response 74 | 75 | @app.route('/.ts') 76 | def tsfile(filename): 77 | response = make_response() 78 | response.headers['Content-Type'] = 'video/MP2T' 79 | response.headers['Access-Control-Allow-Methods'] = 'GET' 80 | response.headers['Access-Control-Allow-Origin'] = '*' 81 | 82 | response.data = open(filename + ".ts").read() 83 | return response 84 | 85 | if __name__ == '__main__': 86 | #app.debug = True 87 | app.run(host="0.0.0.0", threaded=True) 88 | -------------------------------------------------------------------------------- /tools/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/tools/file.png -------------------------------------------------------------------------------- /workspace/mpegts/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clang -Wall gmpegts.c && ./a.out 3 | -------------------------------------------------------------------------------- /workspace/mpegts/cku.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/mpegts/cku.ts -------------------------------------------------------------------------------- /workspace/mpegts/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void hex_print(const char* buf, int len) { 7 | int count = 0; 8 | int i = 0; 9 | 10 | for(i=0; i> 1; 34 | } 35 | 36 | for (i=7; i>=0; i--) { 37 | printf("%c", result[i]); 38 | } 39 | printf("\n"); 40 | } 41 | 42 | char * get_stream_name(int codec_id){ 43 | switch(codec_id){ 44 | case STREAM_TYPE_VIDEO_MPEG1 : 45 | case STREAM_TYPE_AUDIO_MPEG1 : 46 | return "mpeg1"; 47 | break; 48 | case STREAM_TYPE_VIDEO_MPEG2 : 49 | case STREAM_TYPE_AUDIO_MPEG2 : 50 | return "mpeg2"; 51 | break; 52 | case STREAM_TYPE_PRIVATE_SECTION: 53 | return "section"; 54 | break; 55 | case STREAM_TYPE_PRIVATE_DATA : 56 | return "data"; 57 | break; 58 | case STREAM_TYPE_AUDIO_AAC : 59 | return "aac"; 60 | break; 61 | case STREAM_TYPE_AUDIO_AAC_LATM : 62 | return "aac-latm"; 63 | break; 64 | case STREAM_TYPE_VIDEO_MPEG4 : 65 | return "mpeg4"; 66 | break; 67 | case STREAM_TYPE_VIDEO_H264 : 68 | return "h264"; 69 | break; 70 | case STREAM_TYPE_VIDEO_CAVS : 71 | return "cavs"; 72 | break; 73 | case STREAM_TYPE_VIDEO_VC1 : 74 | return "vc1"; 75 | break; 76 | case STREAM_TYPE_VIDEO_DIRAC : 77 | return "dirac"; 78 | break; 79 | case STREAM_TYPE_AUDIO_AC3 : 80 | return "ac3"; 81 | break; 82 | case STREAM_TYPE_AUDIO_DTS : 83 | return "dts"; 84 | break; 85 | default: 86 | return "nil"; 87 | break; 88 | } 89 | } 90 | 91 | char get_stream_type(int codec_id){ 92 | switch(codec_id){ 93 | case STREAM_TYPE_VIDEO_MPEG1 : 94 | case STREAM_TYPE_VIDEO_MPEG2 : 95 | case STREAM_TYPE_VIDEO_MPEG4 : 96 | case STREAM_TYPE_VIDEO_H264 : 97 | case STREAM_TYPE_VIDEO_CAVS : 98 | case STREAM_TYPE_VIDEO_VC1 : 99 | case STREAM_TYPE_VIDEO_DIRAC : 100 | return 'v'; //VIDEO 101 | break; 102 | case STREAM_TYPE_PRIVATE_SECTION: 103 | case STREAM_TYPE_PRIVATE_DATA : 104 | return 'm'; //META 105 | break; 106 | case STREAM_TYPE_AUDIO_MPEG1 : 107 | case STREAM_TYPE_AUDIO_MPEG2 : 108 | case STREAM_TYPE_AUDIO_AAC : 109 | case STREAM_TYPE_AUDIO_AAC_LATM : 110 | case STREAM_TYPE_AUDIO_AC3 : 111 | case STREAM_TYPE_AUDIO_DTS : 112 | return 'a'; //AUDIO 113 | break; 114 | default: 115 | return 'n'; //NIL 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /workspace/mpegts/gmpegts.c: -------------------------------------------------------------------------------- 1 | #include "x100mpegts.h" 2 | #include "common.c" 3 | #include "x100mpegts_debug.c" 4 | #include "x100mpegts_alloc.c" 5 | 6 | void parse_transport_packet_header(transport_packet_header * tph, char * packet) { 7 | tph->transport_error_indicator = packet[1] >> 7; 8 | tph->payload_unit_start_indicator = packet[1] << 1 >> 7; 9 | tph->transport_priority = packet[1] << 2 >> 7; 10 | tph->pid = ( packet[1] << 8 | packet[2] ) << 3 >> 3; 11 | tph->transport_scrambing_control = packet[3] >> 6; 12 | tph->adaption_field_control = packet[3] << 2 >> 6; 13 | tph->continuity_counter = packet[3] << 4 >> 4; 14 | if (tph->adaption_field_control == 2) { 15 | tph->adaptation_fields_length = 183; 16 | } else if (tph->adaption_field_control == 3) { 17 | tph->adaptation_fields_length = packet[4]; 18 | } else { 19 | tph->adaptation_fields_length = 0; 20 | } 21 | } 22 | 23 | void parse_pat(struct unit * unit, struct pid_table * pid_table, struct program_association_table * pat) { 24 | pat->table_id = unit->buffer[0]; 25 | pat->section_syntax_indicator = unit->buffer[1] >> 7; 26 | pat->section_length = ( unit->buffer[1] & 0x03 ) | unit->buffer[2]; 27 | pat->transport_stream_id = unit->buffer[3] << 8 | unit->buffer[4]; 28 | pat->version_number = unit->buffer[5] & 0x3e; 29 | pat->current_next_indicator = unit->buffer[5] & 0x01; 30 | pat->section_number = unit->buffer[6]; 31 | pat->last_section_number = unit->buffer[7]; 32 | pat->program_number = unit->buffer[8] << 8 | unit->buffer[9]; 33 | pat->program_map_pid = (unit->buffer[10] << 8 | unit->buffer[11]) & 0x1fff; 34 | pid_table->pid_pmt = pat->program_map_pid; 35 | debug_program_association_table(pat); 36 | } 37 | 38 | void parse_pmt(struct unit * unit, struct pid_table * pid_table, struct program_map_table * pmt) { 39 | pmt->table_id = unit->buffer[0]; 40 | pmt->section_syntax_indicator = unit->buffer[1] >> 7; 41 | pmt->section_length = ( unit->buffer[1] & 0x03 ) | unit->buffer[2]; 42 | pmt->program_number = unit->buffer[3] << 8 | unit->buffer[4]; 43 | pmt->version_number = unit->buffer[5] & 0x3e; 44 | pmt->current_next_indicator = unit->buffer[5] & 0x01; 45 | pmt->section_number = unit->buffer[6]; 46 | pmt->last_section_number = unit->buffer[7]; 47 | pmt->pcr_pid = (unit->buffer[8] << 8 | unit->buffer[9]) & 0x1fff; 48 | pmt->program_info_length = (unit->buffer[10] << 8 | unit->buffer[11]) & 0x0fff; 49 | 50 | unsigned int type = unit->buffer[12]; 51 | unsigned int epid = (unit->buffer[13] << 8 | unit->buffer[14] ) & 0x1fff; 52 | unsigned int es_info_length = (unit->buffer[15] << 8 | unit->buffer[16] ) & 0x0fff; 53 | printf("type:[%x]", type); 54 | printf("pid:[%x]", epid ); 55 | printf("es_info_length:[%d]", es_info_length ); 56 | 57 | unsigned int o = 5; 58 | unsigned int type2 = unit->buffer[12 + o]; 59 | unsigned int epid2 = (unit->buffer[13+o] << 8 | unit->buffer[14+o] ) & 0x1fff; 60 | unsigned int es_info_length2 = (unit->buffer[15+o] << 8 | unit->buffer[16+o] ) & 0x0fff; 61 | printf("type:[%x]", type2 ); 62 | printf("pid:[%x]", epid2 ); 63 | printf("es_info_length:[%d]", es_info_length2 ); 64 | debug_program_map_table(pmt); 65 | } 66 | 67 | void parse_unit(struct unit * unit, struct pid_table * pid_table, struct program_association_table * pat, struct program_map_table * pmt) { 68 | if(unit->pid == pid_table->pid_pat) { 69 | parse_pat(unit, pid_table, pat); 70 | } else if (unit->pid == pid_table->pid_pmt) { 71 | parse_pmt(unit, pid_table, pmt); 72 | } 73 | } 74 | 75 | int main() { 76 | const char *path = "cku.ts"; 77 | 78 | FILE * fp = fopen(path, "r"); 79 | char buf[TS_PACKET_SIZE]; 80 | 81 | struct transport_packet_header * tph = alloc_tph(); 82 | struct unit * unit = alloc_unit(); 83 | struct pid_table * pid_table = alloc_pid_table(); 84 | struct program_association_table * pat = alloc_pat(); 85 | struct program_map_table * pmt = alloc_pmt(); 86 | int packet_id = 0; 87 | int packet_wanted_number = 100000; 88 | int section_wanted_number = 3; 89 | 90 | while(fread(buf, TS_PACKET_SIZE, 1, fp)) { 91 | if (!packet_wanted_number) 92 | return 0; 93 | 94 | if ( buf[0] != 0x47 ) { 95 | printf("wrong sync_byte\n"); 96 | return -1; 97 | } 98 | 99 | parse_transport_packet_header(tph, buf); 100 | 101 | if (tph->payload_unit_start_indicator == 1 && packet_id > 0) { 102 | if(!section_wanted_number) 103 | return 0; 104 | 105 | parse_unit(unit, pid_table, pat, pmt); 106 | reset_unit(unit); 107 | section_wanted_number--; 108 | } 109 | 110 | int payload_offset = TS_PACKET_HEADER_SIZE + tph->adaptation_fields_length + 1; 111 | int payload_size = TS_PACKET_SIZE - payload_offset; 112 | memcpy(unit->buffer + unit->buffer_offset, buf + payload_offset, payload_size); 113 | unit->buffer_offset += payload_size; 114 | unit->pid = tph->pid; 115 | 116 | packet_id++; 117 | packet_wanted_number--; 118 | 119 | #if DEBUG 120 | //debug_transport_packet_header(tph); 121 | #endif 122 | } 123 | 124 | free_pmt(pmt); 125 | free_pat(pat); 126 | free_tph(tph); 127 | free_unit(unit); 128 | free_pid_table(pid_table); 129 | 130 | return 0; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /workspace/mpegts/pid_pat: -------------------------------------------------------------------------------- 1 | gmpegts.c: if(unit->pid == pid_table->pid_pat) { 2 | gmpegts.c: printf("[%d]=[%d]PAT found\n", unit->pid, pid_table->pid_pat); 3 | x100mpegts_alloc.c: pid_table->pid_pat = 0; // will not change 4 | x100mpegts_alloc.c: pid_table->pid_cat = 1; // will not change 5 | x100mpegts_alloc.c: pid_table->pid_pmt = 0; // will not change 6 | x100mpegts_alloc.c: pid_table->pid_video = 0; // will not change 7 | x100mpegts_alloc.c: pid_table->pid_audio = 0; // will not change 8 | -------------------------------------------------------------------------------- /workspace/mpegts/x100mpegts.h: -------------------------------------------------------------------------------- 1 | #define DEBUG 1 2 | 3 | #define TS_PACKET_SIZE 188 4 | #define UNIT_BUF_SIZE 1024*1024 5 | #define TS_PACKET_HEADER_SIZE 4 6 | 7 | #define STREAM_TYPE_VIDEO_MPEG1 0x01 8 | #define STREAM_TYPE_VIDEO_MPEG2 0x02 9 | #define STREAM_TYPE_AUDIO_MPEG1 0x03 10 | #define STREAM_TYPE_AUDIO_MPEG2 0x04 11 | #define STREAM_TYPE_PRIVATE_SECTION 0x05 12 | #define STREAM_TYPE_PRIVATE_DATA 0x06 13 | #define STREAM_TYPE_AUDIO_AAC 0x0f 14 | #define STREAM_TYPE_AUDIO_AAC_LATM 0x11 15 | #define STREAM_TYPE_VIDEO_MPEG4 0x10 16 | #define STREAM_TYPE_VIDEO_H264 0x1b 17 | #define STREAM_TYPE_VIDEO_CAVS 0x42 18 | #define STREAM_TYPE_VIDEO_VC1 0xea 19 | #define STREAM_TYPE_VIDEO_DIRAC 0xd1 20 | 21 | #define STREAM_TYPE_AUDIO_AC3 0x81 22 | #define STREAM_TYPE_AUDIO_DTS 0x8a 23 | 24 | typedef struct pid_table { 25 | unsigned int pid_pat; 26 | unsigned int pid_cat; 27 | unsigned int pid_pmt; 28 | unsigned int pid_video; 29 | unsigned int video_codec_id; 30 | unsigned int pid_audio; 31 | unsigned int audio_codec_id; 32 | } pid_table; 33 | 34 | typedef struct transport_packet_header { 35 | unsigned int transport_error_indicator:1; 36 | unsigned int payload_unit_start_indicator:1; 37 | unsigned int transport_priority:1; 38 | unsigned int pid:13; 39 | unsigned int transport_scrambing_control:2; 40 | unsigned int adaption_field_control:2; 41 | unsigned int continuity_counter:4; 42 | 43 | unsigned int adaptation_fields_length:8; 44 | } transport_packet_header; 45 | 46 | typedef struct unit { 47 | unsigned char * buffer; 48 | int buffer_offset; 49 | int buffer_total_length; 50 | int pid; 51 | } unit; 52 | 53 | typedef struct program_association_table { 54 | unsigned int table_id:8; 55 | unsigned int section_syntax_indicator:1; 56 | unsigned int :1; 57 | unsigned int reserved0:2; 58 | unsigned int section_length:12; 59 | unsigned int transport_stream_id:16; 60 | unsigned int reserved1:2; 61 | unsigned int version_number:5; 62 | unsigned int current_next_indicator:1; 63 | unsigned int section_number:8; 64 | unsigned int last_section_number:8; 65 | unsigned int program_number:16; 66 | unsigned int :3; 67 | unsigned int program_map_pid:13; 68 | unsigned int crc_32:32; 69 | } program_association_table; 70 | 71 | typedef struct program_map_table { 72 | unsigned int table_id:8; 73 | unsigned int section_syntax_indicator:1; 74 | unsigned int :1; 75 | unsigned int reserved0:2; 76 | unsigned int section_length:12; 77 | unsigned int program_number:16; 78 | unsigned int reserved1:2; 79 | unsigned int version_number:5; 80 | unsigned int current_next_indicator:1; 81 | unsigned int section_number:8; 82 | unsigned int last_section_number:8; 83 | unsigned int reserved2:3; 84 | unsigned int pcr_pid:13; 85 | unsigned int reserved3:4; 86 | unsigned int program_info_length:12; 87 | unsigned int crc_32:32; 88 | } program_map_table; 89 | 90 | -------------------------------------------------------------------------------- /workspace/mpegts/x100mpegts_alloc.c: -------------------------------------------------------------------------------- 1 | struct transport_packet_header * alloc_tph() { 2 | struct transport_packet_header * tph = calloc(1, sizeof(struct transport_packet_header)); 3 | return tph; 4 | } 5 | 6 | void free_tph(struct transport_packet_header * tph) { 7 | free(tph); 8 | } 9 | 10 | struct unit * alloc_unit() { 11 | struct unit * unit = calloc(1, sizeof(struct unit) + 16 ); 12 | unit->buffer = calloc(1, UNIT_BUF_SIZE); 13 | unit->buffer_offset = 0; 14 | unit->buffer_total_length = UNIT_BUF_SIZE; 15 | unit->pid = 0; 16 | return unit; 17 | } 18 | 19 | void reset_unit(struct unit * unit) { 20 | memset(unit->buffer, 0, unit->buffer_total_length); 21 | unit->buffer_offset = 0; 22 | } 23 | 24 | void free_unit(struct unit * unit) { 25 | free(unit->buffer); 26 | free(unit); 27 | } 28 | 29 | struct pid_table * alloc_pid_table() { 30 | struct pid_table * pid_table = calloc(1, sizeof(struct pid_table)); 31 | pid_table->pid_pat = 0; // will not change 32 | pid_table->pid_cat = 1; // will not change 33 | pid_table->pid_pmt = 0; // will not change 34 | pid_table->pid_video = 0; // will not change 35 | pid_table->pid_audio = 0; // will not change 36 | return pid_table; 37 | } 38 | 39 | void free_pid_table(struct pid_table * pid_table) { 40 | free(pid_table); 41 | } 42 | 43 | struct program_association_table * alloc_pat() { 44 | struct program_association_table * pat = calloc(1, sizeof(struct program_association_table)); 45 | memset(pat, 0, sizeof(struct program_association_table)); 46 | return pat; 47 | } 48 | 49 | void free_pat(struct program_association_table * pat) { 50 | free(pat); 51 | } 52 | 53 | struct program_map_table * alloc_pmt() { 54 | struct program_map_table * pmt = calloc(1, sizeof(struct program_map_table)); 55 | memset(pmt, 0, sizeof(struct program_map_table)); 56 | return pmt; 57 | } 58 | 59 | void free_pmt(struct program_map_table * pmt) { 60 | free(pmt); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /workspace/mpegts/x100mpegts_debug.c: -------------------------------------------------------------------------------- 1 | void debug_transport_packet_header( struct transport_packet_header * tph) { 2 | printf("[Packet] "); 3 | printf("transport_error_indicator: %d\t", tph->transport_error_indicator); 4 | printf("payload_unit_start_indicator: %d\t", tph->payload_unit_start_indicator); 5 | printf("transport_priority: %d\t", tph->transport_priority); 6 | printf("PID: %d\t", tph->pid); 7 | printf("transport_scrambing_control: %d\t", tph->transport_scrambing_control); 8 | printf("adaption_field_control: %d\t", tph->adaption_field_control); 9 | printf("continuity_counter: %d\t", tph->continuity_counter); 10 | printf("adaptation_field_length: %d\t", tph->adaptation_fields_length); 11 | printf("\n"); 12 | } 13 | 14 | void debug_program_association_table( struct program_association_table * pat) { 15 | printf("[PAT] "); 16 | printf("table_id: %d\t", pat->table_id); 17 | printf("section_syntax_indicator: %d\t", pat->section_syntax_indicator); 18 | printf("section_length: %d\t", pat->section_length); 19 | printf("transport_stream_id: %d\t", pat->transport_stream_id); 20 | printf("version_number: %d\t", pat->version_number); 21 | printf("current_next_indicator: %d\t", pat->current_next_indicator); 22 | printf("section_number: %d\t", pat->section_number); 23 | printf("last_section_number: %d\t", pat->last_section_number); 24 | printf("program_number: %d\t", pat->program_number); 25 | printf("program_map_pid: %d\t", pat->program_map_pid); 26 | printf("crc_32: %d\t", pat->crc_32); 27 | printf("\n"); 28 | } 29 | 30 | void debug_program_map_table( struct program_map_table * pmt) { 31 | printf("[PMT] "); 32 | printf("table_id: %d\t", pmt->table_id); 33 | printf("section_syntax_indicator: %d\t", pmt->section_syntax_indicator); 34 | printf("section_length: %d\t", pmt->section_length); 35 | printf("program_number: %d\t", pmt->program_number); 36 | printf("version_number: %d\t", pmt->version_number); 37 | printf("current_next_indicator: %d\t", pmt->current_next_indicator); 38 | printf("section_number: %d\t", pmt->section_number); 39 | printf("last_section_number: %d\t", pmt->last_section_number); 40 | printf("pcr_pid: %d\t", pmt->pcr_pid); 41 | printf("program_info_length: %d\t", pmt->program_info_length); 42 | printf("crc_32: %d\t", pmt->crc_32); 43 | printf("\n"); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /workspace/psutil_test.py: -------------------------------------------------------------------------------- 1 | import psutil 2 | 3 | for proc in psutil.process_iter(): 4 | try: 5 | pinfo = proc.as_dict(attrs=['pid', 'name']) 6 | #if pinfo['name'] == 'ffmpeg': 7 | # print(pinfo) 8 | except psutil.NoSuchProcess: 9 | pass 10 | else: 11 | print(pinfo) 12 | #pass 13 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/README.md: -------------------------------------------------------------------------------- 1 | # upload_page 2 | 3 | ---------------------- 4 | 5 | ## 描述 6 | 7 | 视频上传页面 8 | 9 | ## 运行server 10 | 11 | python upload.py 12 | 13 | ### 访问 14 | 15 | http://ip/upload.html 16 | 17 | ## uuid md5 计算方式 18 | 19 | echo -n 'ytOb5FJEYgmKU0epYq_2.jpg' | md5sum 20 | 21 | 108fce3dfaf0f3ff6267e8b2d262dba6 22 | 23 | 访问路径分三级目录, 每级目录顺序去md5 3 位: 24 | 25 | http://hostname/108/fce/3df 26 | 27 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/__pycache__/http.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/__pycache__/http.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/__pycache__/testfile.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/__pycache__/testfile.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/__pycache__/transcoder.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/__pycache__/transcoder.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/conf/transcoder.conf: -------------------------------------------------------------------------------- 1 | [base] 2 | hostname=http://10.221.193.64 3 | [storage] 4 | dir=/tmp/hls/segment 5 | release_dir=/data1/hls/segment 6 | [segment] 7 | time=10 8 | release_path=/tmp/ts/release 9 | expire=65536 10 | vbitrate=105 11 | abitrate=48 12 | fps=15 13 | fps_count=150 14 | [redis] 15 | ip=127.0.0.1 16 | port=6379 17 | [snap] 18 | release_path=/tmp/snap/release 19 | [config] 20 | file=data/transcoder.conf 21 | [url] 22 | add_video_segment=http://10.221.193.64/interface/add_video_segment 23 | video_bitrate_add=http://10.221.193.196:5000/interface/video_uuid_bitrate_add 24 | update_video_status=http://10.221.193.64/interface/update_video_status 25 | update_video_snap_image_count=http://10.221.193.64/interface/update_video_snap_image_count 26 | 27 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/css/x100speed.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #eee; 3 | height: 100%; 4 | text-align: center; 5 | background-color: #222; 6 | } 7 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/http_request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, sys, re, select, subprocess, io, time, shutil, logging 3 | import urllib.request 4 | from x100.x100config import load_config 5 | from x100.x100util import * 6 | from x100.x100request import http_callback, update_video_status 7 | from x100http import X100HTTP, X100Response 8 | #from transcoder import Transcoder 9 | 10 | class Transcoder: 11 | def __init__(self): 12 | self.config = load_config('conf/transcoder.conf') 13 | self.bitrate = int(self.config['segment']['vbitrate']) + int(self.config['segment']['abitrate']) 14 | self._log_config() 15 | 16 | def upload_start(self, req): 17 | print("hello") 18 | 19 | def upload_process(self, key, line): 20 | if key == b'video_id': 21 | video_id = line.decode().rstrip() 22 | self.video_id = video_id 23 | self.init_popen_handler() 24 | 25 | request_info = create_request_info(video_id=self.video_id, status='proceed', bitrate=str(self.bitrate)) 26 | res = http_callback(self.config['url']['update_video_status'], request_info) 27 | self.log(res, self.video_id, 'update_video_status', None) 28 | elif key == b'upload': 29 | self.run_cmd_async(line) 30 | 31 | def upload_finish(self,req): 32 | print("abcdefgh_done") 33 | return "your file uploaded." 34 | 35 | def _log_config(self): 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | def init_popen_handler(self): 39 | cmd = self.build_cmd(self.video_id) 40 | print(cmd) 41 | p = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 42 | self.stdout = p.stdout 43 | self.stdin = p.stdin 44 | self.poll = p.poll() 45 | self.stdout = non_blocking_handler(self.stdout) 46 | return 47 | 48 | def run_cmd_async(self, body): 49 | self.stdin.write(body) 50 | running = self.running() 51 | while running: 52 | line = self.stdout.read(-1) 53 | #segment:'/tmp/a_sd_000030.flv' count:30 endedp=24 drop=0 54 | if line is None: 55 | break 56 | line = line.decode() 57 | ts_re = re.search("segment:\'(.*?)\'\s+count:(\d+).*", line) 58 | if ts_re: 59 | ts_file = ts_re.group(1) 60 | ts_file_index = ts_re.group(2) 61 | ts_filename = ts_file.split('/')[-1] 62 | 63 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], ts_filename, 'ts') 64 | 65 | retcode = self.flv2ts(ts_file, target_file) 66 | if retcode != 0: 67 | logging.error("flv2ts flvfile: %s tsfile: %s failed", ts_file, target_file) 68 | continue 69 | 70 | request_info = self.segment_request_info(target_file, storage_path, ts_file_index) 71 | add_video_segment_url = self.config['url']['add_video_segment'] 72 | 73 | res = http_callback( add_video_segment_url, request_info) 74 | self.log(res, self.video_id, 'add_video_segment', storage_path) 75 | 76 | snap_re = re.search("snap:\'(.*?)\'\s+count:(\d+).*", line) 77 | if snap_re: 78 | snap_img_file = snap_re.group(1) 79 | snap_index = snap_re.group(2) 80 | snap_filename = snap_img_file.split('/')[-1] 81 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], snap_filename, 'snap') 82 | 83 | shutil.move(snap_img_file, target_file) 84 | 85 | info = create_request_info(video_id=self.video_id, snap_image_count=snap_index) 86 | 87 | res = http_callback(self.config['url']['update_video_snap_image_count'], info) 88 | self.log(res, self.video_id, 'update_video_snap_image_count', storage_path) 89 | return 90 | 91 | def flv2ts(self, flvfile, tsfile): 92 | flv2ts_cmd = cmd = "ffmpeg -i " + flvfile +" -c copy -bsf:v h264_mp4toannexb -y "+ tsfile +" &> /dev/null" 93 | retcode = subprocess.check_call(flv2ts_cmd, shell=True) 94 | os.remove(flvfile) 95 | return retcode 96 | 97 | def segment_request_info(self, filepath, storage_path, file_index): 98 | create_time = file_create_time(filepath) 99 | filesize = str(file_size(filepath)) 100 | bitrate = str(self.bitrate) 101 | video_id = self.video_id 102 | hostname = self.config['base']['hostname'] 103 | storage_path = storage_path 104 | frame_count = self.config['segment']['fps_count'] 105 | fragment_id = file_index 106 | 107 | req_info = create_request_info( 108 | video_id=self.video_id, hostname=self.config['base']['hostname'],\ 109 | storage_path=storage_path, frame_count=self.config['segment']['fps_count'],\ 110 | file_size=str(filesize), fragment_id=file_index, bitrate=str(bitrate),\ 111 | fps=self.config['segment']['fps'], create_time=create_time) 112 | return req_info 113 | 114 | def running(self): 115 | return self.poll is None 116 | 117 | def build_cmd(self, video_id): 118 | storage_dir = self.config['storage']['dir'] 119 | if not os.path.exists(storage_dir): 120 | os.makedirs(storage_dir) 121 | 122 | tmp_ts_name = storage_dir + '/' + video_id + "_%d.flv" 123 | tmp_snap_name = storage_dir + '/' + video_id + "_%d.jpg" 124 | vbitrate = self.config['segment']['vbitrate'] 125 | abitrate = self.config['segment']['abitrate'] 126 | segment_time = self.config['segment']['time'] 127 | cmd = "" 128 | cmd += "ffmpeg -v verbose -i -" 129 | cmd += " -filter_complex \"" 130 | cmd += " [0:v:0]fps=15,scale=352:288,split=2[voutA][vtmpB]," 131 | cmd += " [vtmpB]fps=0.5,scale=176:144[voutB],[0:a:0]asplit=1[aoutA]" 132 | cmd += "\" " 133 | cmd += " -map [voutA] -map [aoutA] -c:v libx264 -x264opts bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0" 134 | cmd += " -c:a libfdk_aac -profile:a aac_he -b:a 16k -f segment -segment_format flv -segment_time 10" 135 | cmd += " -y "+ tmp_ts_name +" -map [voutB] -y " + tmp_snap_name + " 2>&1" 136 | 137 | if cmd is not None: 138 | self.cmd = cmd 139 | else: 140 | self.cmd = "" 141 | 142 | return cmd 143 | 144 | def log(self, response, video_id, apiname, filename): 145 | if response['status'] == 'success': 146 | logging.info("video_id: %s snap: %s callbackApi: %s success", video_id, filename, apiname) 147 | print("INFO: video_id: %s snap: %s callbackApi: %s success" % (video_id, filename, apiname)) 148 | else: 149 | logging.error("video_id:%s snap: %s callbackApi: %s error: %s", video_id, filename, apiname, response['message']) 150 | print("ERROR: video_id:%s snap: %s callbackApi: %s error: %s" % (video_id, filename, apiname, response['message']) ) 151 | return 152 | 153 | def __del__(self): 154 | pass 155 | 156 | 157 | app = X100HTTP() 158 | app.upload("/upload", Transcoder) 159 | app.run("0.0.0.0", 8080) 160 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/index.html: -------------------------------------------------------------------------------- 1 | hello world what the fuck 2 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/lib/hls/browser.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // requestAnimationFrame polyfill 5 | window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || setTimeout; 6 | 7 | // preconfiguration using 49 | 50 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/process_info.py: -------------------------------------------------------------------------------- 1 | import subprocess, re 2 | import socket 3 | import urllib.request 4 | import json 5 | 6 | def process_find(find_cmd): 7 | stats = subprocess.Popen(['pidstat','-ruht'], stdout=subprocess.PIPE, close_fds=True).communicate()[0] 8 | 9 | stats_data = stats.splitlines() 10 | del stats_data[0:2] # Deletes system data 11 | 12 | converted_data = [] 13 | for line in stats_data: 14 | if re.search(b'command', line, re.IGNORECASE): 15 | header = line.decode().split() 16 | del header[0] 17 | else: 18 | command = line.decode().split() 19 | cmd = command[-1] 20 | if cmd != find_cmd: 21 | continue 22 | data_dict = dict(zip(header, command)) 23 | 24 | process_memory_mb = float(1000) * float(data_dict["%MEM"].replace(',', '.')) 25 | memory = "{0:.3}".format(process_memory_mb) 26 | memory = memory.replace(",", ".") 27 | 28 | cpu = "{0:.2f}".format( float( data_dict["%CPU"].replace(",", ".") ) ) 29 | cpu = cpu.replace(",", ".") 30 | 31 | command = data_dict["Command"] 32 | if not re.search("_", command, re.IGNORECASE): 33 | extracted_data = { "cpu:%": cpu, 34 | "memory:mb": memory, 35 | "command:" : command} 36 | converted_data.append(extracted_data) 37 | return converted_data 38 | 39 | def get_ip(): 40 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 41 | try: 42 | # doesn't even have to be reachable 43 | s.connect(('10.255.255.255', 0)) 44 | IP = s.getsockname()[0] 45 | except: 46 | IP = '127.0.0.1' 47 | finally: 48 | s.close() 49 | return IP 50 | 51 | def http_callback(url, info): 52 | request_url = url + '?' + info 53 | request = urllib.request.Request(request_url) 54 | with urllib.request.urlopen(request) as f: 55 | return json.loads(f.read().decode('utf-8')) 56 | 57 | if __name__ == "__main__": 58 | process_ffmpeg = process_find('ffmpeg') 59 | process_ffmpeg_count = len(process_ffmpeg) 60 | local_ip = get_ip() 61 | request_info = 'ip=' + local_ip + '&process_count=' + str(process_ffmpeg_count) 62 | res = http_callback('http://10.221.193.64/interface/update_staff_monitor', request_info) 63 | if res['status'] == 'failed': 64 | print('error: %s' % res['message']) 65 | else: 66 | print('success') 67 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/run.py: -------------------------------------------------------------------------------- 1 | from transcoder import Transcoder 2 | from x100http import X100HTTP, X100Response 3 | 4 | 5 | app = X100HTTP() 6 | app.upload("/upload", Transcoder) 7 | app.run("0.0.0.0", 8080) 8 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/transcoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, sys, re, select, subprocess, io, time, shutil, logging 3 | import urllib.request 4 | from x100.x100config import load_config 5 | from x100.x100util import * 6 | from x100.x100request import http_callback, update_video_status 7 | from x100http import X100HTTP, X100Response 8 | #from transcoder import Transcoder 9 | 10 | class Transcoder: 11 | def __init__(self): 12 | self.config = load_config('conf/transcoder.conf') 13 | self.bitrate = int(self.config['segment']['vbitrate']) + int(self.config['segment']['abitrate']) 14 | self._log_config() 15 | 16 | def upload_start(self, req): 17 | print("hello") 18 | 19 | def upload_process(self, key, line): 20 | if key == b'video_id': 21 | video_id = line.decode().rstrip() 22 | self.video_id = video_id 23 | self.init_popen_handler() 24 | 25 | request_info = create_request_info(video_id=self.video_id, status='proceed', bitrate=str(self.bitrate)) 26 | res = http_callback(self.config['url']['update_video_status'], request_info) 27 | self.log(res, self.video_id, 'update_video_status', None) 28 | elif key == b'upload': 29 | self.run_cmd_async(line) 30 | 31 | def upload_finish(self,req): 32 | print("abcdefgh_done") 33 | return "your file uploaded." 34 | 35 | def _log_config(self): 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | def init_popen_handler(self): 39 | cmd = self.build_cmd(self.video_id) 40 | print(cmd) 41 | p = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 42 | self.stdout = p.stdout 43 | self.stdin = p.stdin 44 | self.poll = p.poll() 45 | self.stdout = non_blocking_handler(self.stdout) 46 | return 47 | 48 | def run_cmd_async(self, body): 49 | self.stdin.write(body) 50 | running = self.running() 51 | while running: 52 | line = self.stdout.read(-1) 53 | #segment:'/tmp/a_sd_000030.flv' count:30 endedp=24 drop=0 54 | if line is None: 55 | break 56 | line = line.decode() 57 | ts_re = re.search("segment:\'(.*?)\'\s+count:(\d+).*", line) 58 | if ts_re: 59 | ts_file = ts_re.group(1) 60 | ts_file_index = ts_re.group(2) 61 | ts_filename = ts_file.split('/')[-1] 62 | 63 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], ts_filename, 'ts') 64 | 65 | retcode = self.flv2ts(ts_file, target_file) 66 | if retcode != 0: 67 | logging.error("flv2ts flvfile: %s tsfile: %s failed", ts_file, target_file) 68 | continue 69 | 70 | request_info = self.segment_request_info(target_file, storage_path, ts_file_index) 71 | add_video_segment_url = self.config['url']['add_video_segment'] 72 | 73 | res = http_callback( add_video_segment_url, request_info) 74 | self.log(res, self.video_id, 'add_video_segment', storage_path) 75 | 76 | snap_re = re.search("snap:\'(.*?)\'\s+count:(\d+).*", line) 77 | if snap_re: 78 | snap_img_file = snap_re.group(1) 79 | snap_index = snap_re.group(2) 80 | snap_filename = snap_img_file.split('/')[-1] 81 | (target_file, storage_path) = get_target_file(self.config['storage']['release_dir'], snap_filename, 'snap') 82 | 83 | shutil.move(snap_img_file, target_file) 84 | 85 | info = create_request_info(video_id=self.video_id, snap_image_count=snap_index) 86 | 87 | res = http_callback(self.config['url']['update_video_snap_image_count'], info) 88 | self.log(res, self.video_id, 'update_video_snap_image_count', storage_path) 89 | return 90 | 91 | def flv2ts(self, flvfile, tsfile): 92 | flv2ts_cmd = cmd = "ffmpeg -i " + flvfile +" -c copy -bsf:v h264_mp4toannexb -y "+ tsfile +" &> /dev/null" 93 | retcode = subprocess.check_call(flv2ts_cmd, shell=True) 94 | os.remove(flvfile) 95 | return retcode 96 | 97 | def segment_request_info(self, filepath, storage_path, file_index): 98 | create_time = file_create_time(filepath) 99 | filesize = str(file_size(filepath)) 100 | bitrate = str(self.bitrate) 101 | video_id = self.video_id 102 | hostname = self.config['base']['hostname'] 103 | storage_path = storage_path 104 | frame_count = self.config['segment']['fps_count'] 105 | fragment_id = file_index 106 | 107 | req_info = create_request_info( 108 | video_id=self.video_id, hostname=self.config['base']['hostname'],\ 109 | storage_path=storage_path, frame_count=self.config['segment']['fps_count'],\ 110 | file_size=str(filesize), fragment_id=file_index, bitrate=str(bitrate),\ 111 | fps=self.config['segment']['fps'], create_time=create_time) 112 | return req_info 113 | 114 | def running(self): 115 | return self.poll is None 116 | 117 | def build_cmd(self, video_id): 118 | storage_dir = self.config['storage']['dir'] 119 | if not os.path.exists(storage_dir): 120 | os.makedirs(storage_dir) 121 | 122 | tmp_ts_name = storage_dir + '/' + video_id + "_%d.flv" 123 | tmp_snap_name = storage_dir + '/' + video_id + "_%d.jpg" 124 | vbitrate = self.config['segment']['vbitrate'] 125 | abitrate = self.config['segment']['abitrate'] 126 | segment_time = self.config['segment']['time'] 127 | cmd = "" 128 | cmd += "ffmpeg -v verbose -i -" 129 | cmd += " -filter_complex \"" 130 | cmd += " [0:v:0]fps=15,scale=352:288,split=2[voutA][vtmpB]," 131 | cmd += " [vtmpB]fps=0.5,scale=176:144[voutB],[0:a:0]asplit=1[aoutA]" 132 | cmd += "\" " 133 | cmd += " -map [voutA] -map [aoutA] -c:v libx264 -x264opts bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0" 134 | cmd += " -c:a libfdk_aac -profile:a aac_he -b:a 16k -f segment -segment_format flv -segment_time 10" 135 | cmd += " -y "+ tmp_ts_name +" -map [voutB] -y " + tmp_snap_name + " 2>&1" 136 | 137 | if cmd is not None: 138 | self.cmd = cmd 139 | else: 140 | self.cmd = "" 141 | 142 | return cmd 143 | 144 | def log(self, response, video_id, apiname, filename): 145 | if response['status'] == 'success': 146 | logging.info("video_id: %s snap: %s callbackApi: %s success", video_id, filename, apiname) 147 | print("INFO: video_id: %s snap: %s callbackApi: %s success" % (video_id, filename, apiname)) 148 | else: 149 | logging.error("video_id:%s snap: %s callbackApi: %s error: %s", video_id, filename, apiname, response['message']) 150 | print("ERROR: video_id:%s snap: %s callbackApi: %s error: %s" % (video_id, filename, apiname, response['message']) ) 151 | return 152 | 153 | def __del__(self): 154 | pass 155 | 156 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/transcoder.py.bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, sys, re, select, subprocess, io, time, shutil, logging 3 | import urllib.request 4 | from x100.x100config import load_config 5 | from x100.x100util import md5, file_create_time, file_size, non_blocking_handler, create_request_info 6 | from x100.x100request import http_callback, update_video_status 7 | 8 | class Transcoder: 9 | #def __init__(self, config_file): 10 | # self.config = load_config(config_file) 11 | # self.bitrate = int(self.config['segment']['vbitrate']) + int(self.config['segment']['abitrate']) 12 | # self._log() 13 | def __init__(self): 14 | self.config = load_config('conf/transcoder.conf') 15 | self.bitrate = int(self.config['segment']['vbitrate']) + int(self.config['segment']['abitrate']) 16 | self._log_config() 17 | 18 | def _log_config(self): 19 | logging.basicConfig(level=logging.INFO) 20 | 21 | def handle_body(self, name, body): 22 | if name == b'video_id': 23 | video_id = body.decode().rstrip() 24 | self.video_id = video_id 25 | self.init_popen_handler() 26 | request_info = create_request_info(video_id=self.video_id, status='proceed', bitrate=str(self.bitrate)) 27 | res = http_callback(self.config['url']['update_video_status'], request_info) 28 | self.log(res, self.video_id, 'update_video_status', None) 29 | #if res['status'] == 'failed': 30 | # logging.error("video_id: %s callbackApi: update_video_status error: %s", self.video_id, res['message']) 31 | # return 32 | elif name == b'upload': 33 | self.run_cmd_async(body) 34 | 35 | return 36 | 37 | def init_popen_handler(self): 38 | cmd = self.build_cmd(self.video_id) 39 | print(cmd) 40 | p = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 41 | self.stdout = p.stdout 42 | self.stdin = p.stdin 43 | self.poll = p.poll() 44 | self.stdout = non_blocking_handler(self.stdout) 45 | return 46 | 47 | def target_file(self, filename, file_type): 48 | basedir = self.config['storage']['release_dir'] 49 | md5_str = md5(filename) 50 | 51 | dir1 = md5_str[:3] 52 | dir2 = md5_str[3:6] 53 | dir3 = md5_str[6:9] 54 | 55 | target_dir = basedir + '/' + file_type + '/' + dir1 + '/' + dir2 + '/' + dir3 56 | if not os.path.exists(target_dir): 57 | os.makedirs(target_dir) 58 | 59 | target_filename = target_dir + '/' + filename 60 | storage_path = '/' + dir1 + '/' + dir2 + '/' + dir3 + '/' + filename 61 | 62 | target_filename = target_filename.replace('.flv', '.ts') 63 | storage_path = storage_path.replace('.flv', '.ts') 64 | return (target_filename, storage_path) 65 | 66 | def run_cmd_async(self, body): 67 | self.stdin.write(body) 68 | running = self.running() 69 | while running: 70 | line = self.stdout.read(-1) 71 | #segment:'/tmp/a_sd_000030.flv' count:30 endedp=24 drop=0 72 | if line is None: 73 | break 74 | line = line.decode() 75 | ts_re = re.search("segment:\'(.*?)\'\s+count:(\d+).*", line) 76 | if ts_re: 77 | ts_file = ts_re.group(1) 78 | ts_file_index = ts_re.group(2) 79 | ts_filename = ts_file.split('/')[-1] 80 | 81 | (target_file, storage_path) = self.target_file(ts_filename,'ts') 82 | 83 | flv2ts_cmd = cmd = "ffmpeg -i " + ts_file +" -c copy -bsf:v h264_mp4toannexb -y "+ target_file +" &> /dev/null" 84 | retcode = subprocess.check_call(flv2ts_cmd, shell=True) 85 | if retcode != 0: 86 | logging.error("flv2ts source_file: %s target_file: %s failed", ts_file, target_file) 87 | continue 88 | 89 | request_info = self.segment_request_info(target_file, ts_file_index) 90 | add_video_segment_url = self.config['url']['add_video_segment'] 91 | 92 | res = http_callback( add_video_segment_url, request_info) 93 | self.log(res, self.video_id, 'add_video_segment', storage_path) 94 | 95 | snap_re = re.search("snap:\'(.*?)\'\s+count:(\d+).*", line) 96 | if snap_re: 97 | snap_img_file = snap_re.group(1) 98 | snap_index = snap_re.group(2) 99 | snap_filename = snap_img_file.split('/')[-1] 100 | (target_file, storage_path) = self.target_file(snap_filename, 'snap') 101 | 102 | shutil.move(snap_img_file, target_file) 103 | 104 | info = create_request_info(video_id=self.video_id, snap_image_count=snap_index) 105 | 106 | res = http_callback(self.config['url']['update_video_snap_image_count'], info) 107 | self.log(res, self.video_id, 'update_video_snap_image_count', storage_path) 108 | return 109 | 110 | def segment_request_info(self, filepath, file_index): 111 | create_time = file_create_time(filepath) 112 | filesize = str(file_size(filepath)) 113 | bitrate = str(self.bitrate) 114 | video_id = self.video_id 115 | hostname = self.config['base']['hostname'] 116 | storage_path = filepath 117 | frame_count = self.config['segment']['fps_count'] 118 | fragment_id = file_index 119 | 120 | req_info = create_request_info( 121 | video_id=self.video_id, hostname=self.config['base']['hostname'],\ 122 | storage_path=storage_path, frame_count=self.config['segment']['fps_count'],\ 123 | file_size=str(filesize), fragment_id=file_index, bitrate=str(bitrate),\ 124 | fps=self.config['segment']['fps'], create_time=create_time) 125 | return req_info 126 | 127 | def running(self): 128 | return self.poll is None 129 | 130 | def build_cmd(self, video_id): 131 | #target_ts_name, target_snap_name = self.target_filename() 132 | storage_dir = self.config['storage']['dir'] 133 | if not os.path.exists(storage_dir): 134 | os.makedirs(storage_dir) 135 | 136 | tmp_ts_name = storage_dir + '/' + video_id + "_%d.flv" 137 | tmp_snap_name = storage_dir + '/' + video_id + "_%d.jpg" 138 | vbitrate = self.config['segment']['vbitrate'] 139 | abitrate = self.config['segment']['abitrate'] 140 | segment_time = self.config['segment']['time'] 141 | cmd = "" 142 | cmd += "ffmpeg -v verbose -i -" 143 | cmd += " -filter_complex \"" 144 | cmd += " [0:v:0]fps=15,scale=352:288,split=2[voutA][vtmpB]," 145 | cmd += " [vtmpB]fps=0.5,scale=176:144[voutB],[0:a:0]asplit=1[aoutA]" 146 | cmd += "\" " 147 | cmd += " -map [voutA] -map [aoutA] -c:v libx264 -x264opts bitrate=450:no-8x8dct:bframes=0:no-cabac:weightp=0:no-mbtree:me=dia:no-mixed-refs:partitions=i8x8,i4x4:rc-lookahead=0:ref=1:subme=1:trellis=0" 148 | cmd += " -c:a libfdk_aac -profile:a aac_he -b:a 16k -f segment -segment_format flv -segment_time 10" 149 | cmd += " -y "+ tmp_ts_name +" -map [voutB] -y " + tmp_snap_name + " 2>&1" 150 | 151 | if cmd is not None: 152 | self.cmd = cmd 153 | else: 154 | self.cmd = "" 155 | 156 | return cmd 157 | 158 | def log(self, response, video_id, apiname, filename): 159 | if response['status'] == 'success': 160 | logging.info("video_id: %s snap: %s callbackApi: %s success", video_id, filename, apiname) 161 | else: 162 | logging.error("video_id:%s snap: %s callbackApi: %s error: %s", video_id, filename, apiname, response['message']) 163 | return 164 | 165 | def __del__(self): 166 | #self.stdin.close() 167 | #while True: 168 | # content = self.stdout.read(-1) 169 | # if not content: 170 | # break 171 | # print(content) 172 | ##video_status_set(url, video_id, status, bitrate=None): 173 | #res = update_video_status(self.config['url']['update_video_status'], self.video_id, 'success') 174 | #if res['status'] == 'failed': 175 | # logging.error('video_id: %s error: %s', self.video_id, res['message']) 176 | #return 177 | pass 178 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 上传视频 6 | 7 | 8 | 9 | 10 | 11 |
12 | 23 |
24 | 25 |
26 |
27 |
28 |

上传视频

29 |
30 | 31 | 32 |
33 | 34 |

35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 | 47 | 48 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 219 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/upload.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler, HTTPServer 2 | import socketserver 3 | import time 4 | import os 5 | from transcoder import Transcoder 6 | from x100daemon import Daemon 7 | 8 | 9 | 10 | class MyServer(BaseHTTPRequestHandler): 11 | 12 | def _get_mime(self, path): 13 | if os.path.splitext(path)[1] == '.css': 14 | return "text/css" 15 | elif os.path.splitext(path)[1] == '.ico': 16 | return "image/x-icon" 17 | elif os.path.splitext(path)[1] == '.html': 18 | return "text/html" 19 | return "" 20 | 21 | def _get_abs_filepath(self, path): 22 | file_path = os.path.basename(path) 23 | if file_path == "": 24 | file_path = "index.html" 25 | return file_path 26 | 27 | def _handle_body(self, line): 28 | self._handle_class.handle_body(self._content_key, line) 29 | 30 | def _handle_a_line(self, line): 31 | boundary = str.encode(self._boundary) 32 | if line.find(boundary) != -1: 33 | self._status_content = "HEAD" 34 | return 35 | 36 | if line == b"\r\n": 37 | self._status_content = "BODY" 38 | return 39 | 40 | if self._status_content == "HEAD": 41 | [http_header_name, http_header_value] = line.split(b": ") 42 | if http_header_name == b'Content-Disposition': 43 | values = http_header_value.split(b"; ") 44 | for value in values: 45 | if value.find(b"=\"") == -1: 46 | continue; 47 | [header_name, header_value] = value.split(b"=\"") 48 | if header_name == b'name': 49 | [content_key, foo] = header_value.split(b"\"") 50 | self._content_key = content_key 51 | 52 | elif self._status_content == "BODY": 53 | self._handle_body(line) 54 | 55 | def do_GET(self): 56 | file_path = self._get_abs_filepath(self.path) 57 | f = open(file_path, "rb", buffering=0) 58 | 59 | self.send_response(200) 60 | self.send_header("Content-type", self._get_mime(self.path)) 61 | self.end_headers() 62 | self.wfile.write(f.readall()) 63 | 64 | def do_POST(self): 65 | self._handle_class = Transcoder() 66 | content_length = int(self.headers.get_all('Content-Length')[0]) 67 | content_type = self.headers.get_all('Content-Type')[0] 68 | self._boundary = content_type.split('boundary=', maxsplit=1)[1] 69 | self.buf = b'' 70 | self._status_content = "HEAD" # HEAD, BODY 71 | self._content_key = "" 72 | 73 | new_line = '' 74 | buf = '' 75 | 76 | while content_length > 0: 77 | want_byte = 4096 78 | if content_length > want_byte: 79 | buf = self.rfile.read(want_byte) 80 | else: 81 | buf = self.rfile.read(content_length) 82 | content_length -= want_byte 83 | self.buf = self.buf + buf 84 | 85 | while True: 86 | line_end = self.buf.find(b'\r\n') 87 | if line_end == -1: 88 | break 89 | else: 90 | self._handle_a_line(self.buf[:line_end + 2]) 91 | self.buf = self.buf[line_end + 2:] 92 | 93 | self.send_response(200) 94 | self.send_header("Content-type", "text/html") 95 | self.end_headers() 96 | 97 | 98 | class ForkingHTTPServer(socketserver.ForkingMixIn, HTTPServer): 99 | 100 | def finish_request(self, request, client_address): 101 | HTTPServer.finish_request(self, request, client_address) 102 | 103 | 104 | if __name__ == "__main__": 105 | #d = Daemon('/var/run/transcoder.pid') 106 | #d.daemonize() 107 | try: 108 | s = ForkingHTTPServer(("0.0.0.0", 80), MyServer) 109 | s.serve_forever() 110 | except KeyboardInterrupt: 111 | s.socket.close() 112 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/__pycache__/x100config.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/x100/__pycache__/x100config.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/__pycache__/x100http.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/x100/__pycache__/x100http.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/__pycache__/x100request.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/x100/__pycache__/x100request.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/__pycache__/x100util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svideo/videostack/e302d773becad7c4046f697bd66e2ffedf671217/workspace/upload_page/x100uploadpage/x100/__pycache__/x100util.cpython-34.pyc -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/x100config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, configparser 3 | 4 | def load_config(configfile): 5 | configfile = 'conf/transcoder.conf' 6 | if not os.path.exists(configfile): 7 | print("no config file") 8 | sys.exit(1) 9 | config = configparser.ConfigParser() 10 | config.read(configfile) 11 | return config 12 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/x100http.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib 3 | import json 4 | from x100.x100util import create_request_info 5 | 6 | def http_callback(url, info): 7 | request_url = url + '?' + info 8 | request = urllib.request.Request(request_url) 9 | with urllib.request.urlopen(request) as f: 10 | return json.loads(f.read().decode('utf-8')) 11 | 12 | def video_bitrate_add(url, uuid, bitrate): 13 | #url = 'http://10.221.193.196:5000/interface/video_uuid_bitrate_add' 14 | #http://10.221.193.196:5000/interface/video_uuid_bitrate_add?uuid=ytE3V3GyJigi2sqeBK&bitrate=20 15 | info = 'bitrate=' + bitrate + '&uuid=' + uuid 16 | return http_callback(url, info) 17 | 18 | def update_video_status(url, video_id, status, bitrate=None): 19 | if bitrate is not None: 20 | info = create_request_info(video_id=video_id, bitrate=bitrate, status=status) 21 | else: 22 | info = create_request_info(video_id=video_id, status=status) 23 | print("xxxxxxxxxxxxxxxxxxxxxx") 24 | print(url) 25 | print(info) 26 | print("xxxxxxxxxxxxxxxxxxxxxx") 27 | return http_callback(url, info) 28 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/x100redis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/evn python 2 | import x100redis 3 | 4 | def redis_conn(self): 5 | r = redis.StrictRedis(host=self.config['redis']['ip'], port=self.config['redis']['port'], db=0) 6 | return r 7 | 8 | def insert_redis(self, score, member): 9 | r = self.redis_conn() 10 | zz_key = self.segment_list_name 11 | print(self.segment_list_name) 12 | print(member) 13 | r.zadd(zz_key, score, member) 14 | r.expire(zz_key, self.config['segment']['expire']) 15 | info("insert redis ok") 16 | return 1 17 | 18 | def remove_redis(self, zz_key, member): 19 | r = self.redis_conn() 20 | zz_key = self.segment_list_name 21 | r.zrem(zz_key, member) 22 | info("delete redis ok") 23 | return 1 24 | 25 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/x100request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib 3 | import json 4 | from x100.x100util import request_info_serialize 5 | 6 | def http_callback(url, info): 7 | request_url = url + '?' + info 8 | request = urllib.request.Request(request_url) 9 | with urllib.request.urlopen(request, timeout=1) as f: 10 | return json.loads(f.read().decode('utf-8')) 11 | 12 | def video_bitrate_add(url, uuid, bitrate): 13 | #url = 'http://10.221.193.196:5000/interface/video_uuid_bitrate_add' 14 | #http://10.221.193.196:5000/interface/video_uuid_bitrate_add?uuid=ytE3V3GyJigi2sqeBK&bitrate=20 15 | info = 'bitrate=' + bitrate + '&uuid=' + uuid 16 | return http_callback(url, info) 17 | 18 | def update_video_status(url, video_id, status, bitrate=None): 19 | if bitrate is not None: 20 | info = request_info_serialize(video_id=video_id, bitrate=bitrate, status=status) 21 | else: 22 | info = request_info_serialize(video_id=video_id, status=status) 23 | return http_callback(url, info) 24 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100/x100util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import hashlib, os, sys 3 | from fcntl import fcntl, F_GETFL, F_SETFL 4 | from os import O_NONBLOCK 5 | 6 | def md5(filename): 7 | m = hashlib.md5() 8 | m.update(filename.encode()) 9 | md5_str = m.hexdigest() 10 | return md5_str 11 | 12 | def file_create_time(afile): 13 | if not os.path.exists(afile): 14 | print("file %s not exist" % afile) 15 | sys.exit(1) 16 | return str(int(os.path.getctime(afile))) 17 | 18 | def file_size(afile): 19 | if not os.path.exists(afile): 20 | print("file %s not exist" % afile) 21 | sys.exit(1) 22 | statinfo = os.stat(afile) 23 | return statinfo.st_size 24 | 25 | def non_blocking_handler(handler): 26 | flags = fcntl(handler, F_GETFL) 27 | fcntl(handler, F_SETFL, flags | O_NONBLOCK) 28 | return handler 29 | 30 | def create_request_info(**kwargs): 31 | info = "" 32 | for k, v in kwargs.items(): 33 | info += k + '=' + v + '&' 34 | return info 35 | 36 | def get_target_file(release_dir, filename, file_type): 37 | 38 | md5_str = md5(filename) 39 | 40 | dir1 = md5_str[:3] 41 | dir2 = md5_str[3:6] 42 | dir3 = md5_str[6:9] 43 | 44 | target_dir = release_dir + '/' + file_type + '/' + dir1 + '/' + dir2 + '/' + dir3 45 | if not os.path.exists(target_dir): 46 | os.makedirs(target_dir) 47 | 48 | target_filename = target_dir + '/' + filename 49 | storage_path = '/' + dir1 + '/' + dir2 + '/' + dir3 + '/' + filename 50 | 51 | target_filename = target_filename.replace('.flv', '.ts') 52 | storage_path = storage_path.replace('.flv', '.ts') 53 | return (target_filename, storage_path) 54 | -------------------------------------------------------------------------------- /workspace/upload_page/x100uploadpage/x100speed.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #eee; 3 | height: 100%; 4 | text-align: center; 5 | background-color: #222; 6 | } 7 | -------------------------------------------------------------------------------- /workspace/while.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from x100daemon import Daemon 3 | import time, sys 4 | 5 | 6 | d = Daemon('/tmp/a.pid') 7 | d.daemonize() 8 | 9 | count = 0 10 | while True: 11 | time.sleep(1) 12 | print(count) 13 | count += 1 14 | if count == 10: 15 | sys.exit(1) 16 | 17 | --------------------------------------------------------------------------------