ffplayout是什么?全家桶又是什么?
ffplayout主页
ffplayout是一个24/7在线推流的工具,借助ffplayout可以实现24小时直播,比如音乐电台之类。
主要的功能由ffplayout_engine实现,其实原理上就是一个python程序,控制两个ffmpeg,一个对原始媒体解码,另一个编码并输出。目前ffplayout更新到v4.0.0,根据github页面的说明,以后会从python迁移到rust,性能上应该可以有所提升。
全家桶则在engine的基础上加入了ffplayout-frontend和ffplayout-api,engine本身就可以实现核心的功能,但是修改配置、播放列表等需要直接修改文件。通过frontend和api可以从网页端直接控制engine,方便很多。
官方推荐的rtmp服务器是SRS,但我已经有owncast,就不另外部署了。
部署方法
官方提供了安装脚本,每一个组件repo里也都有对应的手动安装方法,不过我更希望可以用docker部署。
官方虽然提供了docker部署方法,但是这个repo基本没有更新,说明也不全,没办法拿来就用,所以我记录一下docker部署的方法。
虽说官方的docker部署repo不能直接用,作为一个starting point还是很适合的,用git拉取到本地,更新/修改一些内容后就可以成功部署。
拉取代码到本地后,先按需要修改docker-compose.yml,比如注释掉image,去掉SRS等,另外我因为一开始部署总是不成功,添加了一些log相关的路径。
---
version: '3.6'
services:
# srs:
# container_name: srs
# image: ossrs/srs:3
# volumes:
# - "./config/srs.conf:/usr/local/srs/conf/srs.conf"
# - "/etc/localtime:/etc/localtime:ro"
# - "/etc/timezone:/etc/timezone:ro"
# - hls_vol:/usr/local/srs/objs/nginx/html/live
# networks:
# - ffplayout-network
# ports:
# - "1935:1935"
# - "1985:1985"
# restart: unless-stopped
ffplayout-engine:
container_name: ffplayout-engine
# image: ghetzel/ffplayout-engine:3.0
build: ./ffplayout-engine
volumes:
- "./config/engine:/etc/ffplayout"
- "./assets/fonts:/usr/share/fonts"
- "./logs:/var/log/ffplayout"
- "./tv-media:/tv-media"
- "./playlists:/playlists"
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
- "./hls_vol:/var/www/live"
networks:
- ffplayout-network
ports:
- "5555:5555"
- "9001:9001"
restart: unless-stopped
# depends_on:
# - srs
ffplayout-api:
container_name: ffplayout-api
# image: ghetzel/ffplayout-api:3.0
build: ./ffplayout-api
volumes:
- "./config/engine:/etc/ffplayout"
- "./logs:/var/log/ffplayout"
- "./tv-media:/tv-media"
- "./playlists:/playlists"
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
- "./assets/static:/opt/ffplayout-api/ffplayout/static"
- "./assets/dbs:/opt/ffplayout-api/ffplayout/dbs"
networks:
- ffplayout-network
ports:
- "127.0.0.1:8001:8001"
restart: unless-stopped
depends_on:
- ffplayout-engine
ffplayout-frontend:
container_name: ffplayout-frontend
# image: ghetzel/ffplayout-frontend:3.0
build: ./ffplayout-frontend
volumes:
- "./tv-media:/usr/share/nginx/html/tv-media"
- "./config/frontend.conf:/etc/nginx/conf.d/default.conf"
- "./assets/static:/usr/share/nginx/html/static"
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
- "./hls_vol:/usr/share/nginx/html/live:ro"
networks:
- ffplayout-network
ports:
- "127.0.0.1:3000:80"
restart: unless-stopped
depends_on:
- ffplayout-api
volumes:
hls_vol:
driver_opts:
type: tmpfs
device: tmpfs
o: "size=1536M"
networks:
ffplayout-network:
name: ffplayout-network
driver: bridge
engine相关配置
先修改engine部分,即使frontend和api不能成功运行,光靠engine也已经可以实现24/7播放。
首先修改./ffplayout-engine/Dockerfile,把版本号改成需要的版本,其他部分不需要修改。
然后在./config/engine中修改配置文件ffplayout-001.yml,这里可以添加多个配置,同时运行多个ffplayout,添加ffplayout配置后在子文件夹supervisor中添加对应的配置即可。
这个文件是engine对应的配置,拉取到的代码是没有更新过的老文件,这里需要到engine的repo中获取需要的配置文件,然后按需求修改,再覆盖ffplayout-001.yml。
输入、输出等设置都在这个配置文件里,如果设置正确,应该可以直接进行直播。
接下来修改supervisor配置,supervisord.conf中,修改password,这是访问supervior监控页面和socket的密码。如果需要生成相关log,在engine-001.conf和supervisord.conf对应的位置设置即可。
api相关配置
api是最复杂的,但如果没有api,frontend也无法正常运行,所以只能先配置api。
我直接重写了Dockerfile,原来的Dockerfile无论怎么改都没法成功部署,系统我也从alpine换成了debian,基本上照搬api的手动部署教程。timeZone,secret,domainName之类的按自己需要修改即可。其中RUN sed -i "s/SOCKET_PASS = 'hsF0wQkl5zopEy1mBlT3g'/SOCKET_PASS = 'xxxxxxxxxxxxx'/g" ffplayout/settings/common.py
当中的xxxxxxxxxxxxx需要改成supervior配置中的密码。
FROM python:3.9-buster
ENV DJANGO_SETTINGS_MODULE=ffplayout.settings.production
ARG version=4.0.0
ARG timeZone=Etc/UTC
ARG domainName=ffplayout.stsecurity.moe
ARG serviceUser=root
# get ffplayout-api and all dependencies
RUN apt-get update
RUN apt-get install -y gcc tzdata libffi-dev mediainfo
WORKDIR /opt
RUN wget "https://github.com/ffplayout/ffplayout-api/archive/v${version}.zip"
RUN unzip "./v${version}.zip"
RUN mv "./ffplayout-api-${version}" './ffplayout-api'
RUN rm "v${version}.zip"
WORKDIR /opt/ffplayout-api
RUN pip install --no-cache-dir -r ./requirements-base.txt
RUN mkdir /etc/ffplayout
WORKDIR /opt/ffplayout-api/ffplayout
ARG secret=xxxxxxxxxxxxxxx
RUN sed -i "s/---a-very-important-secret-key-_-generate-it-new---/$secret/g" ffplayout/settings/production.py
RUN sed -i "s/'localhost'/'localhost', \'$domainName\'/g" ffplayout/settings/production.py
RUN sed -i "s/ffplayout\\.local/$domainName\',\n \'https\\:\/\/$domainName/g" ffplayout/settings/production.py
RUN sed -i "s|TIME_ZONE = 'UTC'|TIME_ZONE = '$timeZone'|g" ffplayout/settings/common.py
RUN sed -i "s/localhost/$domainName/g" ../docs/db_data.json
RUN sed -i "s|MULTI_CHANNEL = False|MULTI_CHANNEL = True|g" ffplayout/settings/common.py
RUN sed -i "s/USE_SOCKET = False/USE_SOCKET = True/g" ffplayout/settings/common.py
RUN sed -i "s/SOCKET_IP = 'localhost'/SOCKET_IP = 'ffplayout-engine'/g" ffplayout/settings/common.py
RUN sed -i "s/SOCKET_PASS = 'hsF0wQkl5zopEy1mBlT3g'/SOCKET_PASS = 'xxxxxxxxxxxxx'/g" ffplayout/settings/common.py
RUN sed -i "s/stream.m3u8/tv.m3u8/g" ../docs/db_data.json
COPY ./assets/start.sh /start.sh
WORKDIR /opt/ffplayout-api/ffplayout
EXPOSE 8001
ENTRYPOINT ["/start.sh"]
然后修改./ffplayout-api/assets文件夹的start.sh,这是api容器的entrypoint。
首先记得把第一行改成#!/bin/bash,不然用sh运行的话会报错。第一个if块会在没有数据库的时候运行,其中有一步会创建管理员账户,username不能通过frontend修改,只能在这里设置好(或者运行之后再进容器里改,太麻烦了)。最后一行是gunicorn的运行命令,我没什么经验,不过这里可以设置log输出路径,有需要的话可以设置。
#!/bin/bash
if [[ ! -f './dbs/player.sqlite3' ]]; then
python manage.py makemigrations
python manage.py migrate
python manage.py loaddata ../docs/db_data.json
python manage.py shell -c "from django.contrib.auth.models import User; User.objects.create_superuser('username', 'email', 'password')"
sed -i "s|TIME_ZONE = 'UTC'|TIME_ZONE = '$(cat /etc/timezone)'|g" /opt/ffplayout-api/ffplayout/ffplayout/settings/common.py
fi
if [[ ! -d './static/rest_framework' ]]; then
python manage.py collectstatic --noinput
fi
/usr/local/bin/gunicorn --workers 4 --worker-class=gevent --env DJANGO_SETTINGS_MODULE=ffplayout.settings.production --timeout 10800 --log-level=info --log-file=- --access-logfile=- --bind 0.0.0.0:8001 ffplayout.wsgi:application
frontend相关配置
frontend的Dockerfile需要修改版本号,并且按情况修改BASE_URL和API_URL,我把API_URL设置为/apireq/在反代时手动分流了一下,这样我就能看出哪些request是来自frontend到api的,实际使用中API_URL保留默认应该就可以,反代规则用官方提供的location ~ ^/(api|admin|auth|api-auth)
即可。
frontend容器运行时会运行一个nginx服务,配置在./config/frontend.conf,可以不用修改。
理论上讲可以简化一下这个容器,不运行Nginx,只通过反代的Nginx来serve,不过我懒得试了,也需要重写Dockerfile,不太方便。
Nginx反代
反代配置主要是分流frontend和api,如果用默认的API_URL设置,把/apireq部分的规则改为location ~ ^/(api|admin|auth|api-auth)
,去掉rewrite,应该就可以了。api部分的header配置来自api的repo内docs文件夹中的ffplayout-api.conf。
conf文件:
server {
listen 80;
listen [::]:80;
server_name ffplayout.stsecurity.moe;
access_log /var/log/nginx/ffplayout-access.log;
error_log /var/log/nginx/ffplayout-error.log;
root /var/www/html;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ffplayout.stsecurity.moe;
access_log /var/log/nginx/ffplayout-access.log;
error_log /var/log/nginx/ffplayout-error.log;
# Uncomment these lines once you acquire a certificate:
ssl_certificate /etc/nginx/ssl/ffplayoutfullchain.cer;
ssl_certificate_key /etc/nginx/ssl/ffplayoutkey.key;
root /var/www/html;
location /apireq {
rewrite ^/apireq(.*)$ $1 break;
try_files $uri @apiproxy;
}
location / {
add_header 'Access-Control-Allow-Origin' '*';
try_files $uri @proxy;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://localhost:3000;
}
location @apiproxy {
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
etag off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 36000s;
proxy_connect_timeout 36000s;
proxy_send_timeout 36000s;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
send_timeout 36000s;
proxy_no_cache 1;
proxy_pass http://localhost:8001;
}
}
大功告成
docker-compose build && docker-compose up -d运行。
Comments NOTHING