├── .github └── workflows │ └── build_publish.yml ├── .gitlab-ci.yml ├── Dockerfile ├── Dockerfile.nvidia ├── README.md ├── docker-compose.yml ├── entrypoint.sh ├── nginx.conf ├── player.html ├── profiles ├── abr_transcoding_hd.sh ├── abr_transcoding_hd_1080p.sh ├── abr_transcoding_sd.sh ├── passthrough.sh ├── transcoding_hd.sh └── transcoding_sd.sh ├── profiles_nvidia ├── .helpers.sh ├── abr_transcoding_hd.sh ├── abr_transcoding_hd_1080p.sh ├── abr_transcoding_sd.sh ├── passthrough.sh ├── transcoding_hd.sh └── transcoding_sd.sh └── supervisord.conf /.github/workflows/build_publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish image 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | name: Build and publish image job 15 | permissions: 16 | contents: read 17 | packages: write 18 | steps: 19 | - name: Checkout master 20 | uses: actions/checkout@master 21 | - name: Build and publish image 22 | uses: ilteoood/docker_buildx@master 23 | with: 24 | tag: latest 25 | imageName: ${{ env.REGISTRY }}/${{ github.repository }} 26 | platform: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7 27 | publish: true 28 | dockerServer: ${{ env.REGISTRY }} 29 | dockerUser: ${{ github.actor }} 30 | dockerPassword: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | default: 2 | image: docker:19.03.12 3 | services: 4 | - docker:19.03.12-dind 5 | before_script: 6 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 7 | 8 | stages: 9 | - build 10 | 11 | build: 12 | stage: build 13 | variables: 14 | IMAGE_TAG: $CI_REGISTRY_IMAGE:latest 15 | script: 16 | - docker build -t $IMAGE_TAG . 17 | - docker push $IMAGE_TAG 18 | only: 19 | - master 20 | 21 | build nivdia: 22 | stage: build 23 | variables: 24 | IMAGE_TAG: $CI_REGISTRY_IMAGE:nivdia 25 | script: 26 | - docker build --build-arg "BASE_IMAGE=${NVIDIA_FFMPEG_IMAGE}" -t $IMAGE_TAG -f Dockerfile.nvidia . 27 | - docker push $IMAGE_TAG 28 | only: 29 | - master 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | WORKDIR /usr/src 4 | 5 | RUN set -eux; apt-get update; \ 6 | apt-get install -y --no-install-recommends ffmpeg nginx supervisor; \ 7 | # 8 | # clean up 9 | rm -rf /var/lib/apt/lists/*; 10 | 11 | ARG USERNAME=user 12 | ARG USER_UID=1000 13 | ARG USER_GID=$USER_UID 14 | 15 | RUN mkdir -p /var/log/supervisor; \ 16 | mkdir -p /var/www/html; \ 17 | groupadd --gid $USER_GID $USERNAME; \ 18 | useradd --uid $USER_UID --gid $USERNAME --shell /bin/bash --create-home $USERNAME; \ 19 | chown $USERNAME /var/www/html; 20 | 21 | COPY supervisord.conf entrypoint.sh ./ 22 | COPY profiles profiles 23 | COPY player.html /var/www/html/index.html 24 | COPY nginx.conf /etc/nginx/sites-enabled/default 25 | 26 | ENV USER=$USERNAME 27 | 28 | CMD ["./entrypoint.sh"] 29 | -------------------------------------------------------------------------------- /Dockerfile.nvidia: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=willprice/nvidia-ffmpeg 2 | FROM $BASE_IMAGE as base 3 | 4 | WORKDIR /usr/src 5 | 6 | RUN set -eux; apt-get update; \ 7 | apt-get install -y --no-install-recommends nginx supervisor; \ 8 | # 9 | # clean up 10 | rm -rf /var/lib/apt/lists/*; 11 | 12 | ARG USERNAME=user 13 | ARG USER_UID=1000 14 | ARG USER_GID=$USER_UID 15 | 16 | RUN mkdir -p /var/log/supervisor; \ 17 | mkdir -p /var/www/html; \ 18 | groupadd --gid $USER_GID $USERNAME; \ 19 | useradd --uid $USER_UID --gid $USERNAME --shell /bin/bash --create-home $USERNAME; \ 20 | chown $USERNAME /var/www/html; 21 | 22 | COPY supervisord.conf entrypoint.sh ./ 23 | COPY profiles_nvidia profiles 24 | COPY player.html /var/www/html/index.html 25 | COPY nginx.conf /etc/nginx/sites-enabled/default 26 | 27 | ENV USER=$USERNAME 28 | 29 | ENV NVIDIA_VISIBLE_DEVICES all 30 | ENV NVIDIA_DRIVER_CAPABILITIES compute,video,utility 31 | 32 | ENTRYPOINT ["./entrypoint.sh"] 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hls-restream 2 | Restream live content as HLS using ffmpeg in docker. 3 | 4 | # Usage 5 | 6 | You need to have docker & docker-compose installed. 7 | 8 | 1. Clone this repository: 9 | ```sh 10 | git clone https://github.com/m1k1o/hls-restream 11 | cd hls-restream 12 | ``` 13 | 14 | 2. Modify `docker-compose.yml` and add your own sources: 15 | ```yml 16 | version: "3.4" 17 | services: 18 | hls: 19 | build: "./" 20 | container_name: "hls" 21 | restart: "always" 22 | tmpfs: 23 | - "/var/www/html:mode=777,size=128M,uid=1000,gid=1000" 24 | ports: 25 | - "80:80" 26 | environment: 27 | PROFILE: passthrough 28 | SOURCES: | 29 | # SD Channels: 30 | ch1 rtsp://192.168.1.5:554/ch1 31 | ch2 rtsp://192.168.1.5:554/ch2 32 | # HD Channels: 33 | ch1_hd http://192.168.1.6/stream/channelid/8967896?profile=pass 34 | ch2_hd http://192.168.1.6/stream/channelid/4969639?profile=pass 35 | ``` 36 | 37 | Profiles can you find in `profiles/` folder. 38 | * passthrough - default, no transcoding. 39 | 40 | H264 adaptive bitrate transcoding: 41 | * abr_transcoding_sd - 360p and 480p 42 | * abr_transcoding_hd - 360p, 480p and 720p 43 | * abr_transcoding_hd_1080p - 360p, 480p, 720p and 1080p 44 | 45 | H264 transcoding: 46 | * transcoding_hd - 720p only 47 | * transcoding_sd - 480p only 48 | 49 | 3. Start docker compose (run this everytime you modify stream sources): 50 | ```sh 51 | docker-compose up -d --build 52 | ``` 53 | 54 | 4. Watch streams: 55 | ``` 56 | http://localhost/ch1.m3u8 57 | http://localhost/ch2.m3u8 58 | http://localhost/ch1_hd.m3u8 59 | http://localhost/ch2_hd.m3u8 60 | ``` 61 | 62 | In case of error, troubleshoot: 63 | 64 | ```sh 65 | docker logs hls 66 | docker exec -it hls sh -c 'cd /var/log/supervisor && /bin/bash' 67 | ``` 68 | 69 | # NVIDIA GPU hardware acceleration 70 | 71 | You will need to have [nvidia-docker](https://github.com/NVIDIA/nvidia-docker) installed. 72 | 73 | ``` 74 | docker build -t hls_nvidia -f Dockerfile.nvidia . 75 | docker run -d --gpus=all \ 76 | --name hls_nvidia \ 77 | --tmpfs /var/www/html:mode=777,size=128M,uid=1000,gid=1000 \ 78 | -p 80:80 \ 79 | -e 'PROFILE=transcoding_sd' \ 80 | -e 'SOURCES= 81 | # SD Channels: 82 | ch1 rtsp://192.168.1.5:554/ch1 83 | ch2 rtsp://192.168.1.5:554/ch2 84 | # HD Channels: 85 | ch1_hd http://192.168.1.6/stream/channelid/8967896?profile=pass 86 | ch2_hd http://192.168.1.6/stream/channelid/4969639?profile=pass 87 | ' \ 88 | hls_nvidia 89 | ``` 90 | 91 | ## Supported inputs 92 | 93 | Input codec will be automatically determined from given stream. Please check your graphic card's supported codec and maximum concurrent sessions [here](https://developer.nvidia.com/video-encode-decode-gpu-support-matrix). 94 | 95 | | Codec | CUVID | Codec Name | 96 | | ---------- | ----------- | ----------------------------------------- | 97 | | h264 | h264_cuvid | H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 | 98 | | hevc | hevc_cuvid | H.265 / HEVC | 99 | | mjpeg | mjpeg_cuvid | Motion JPEG | 100 | | mpeg1video | mpeg1_cuvid | MPEG-1 video | 101 | | mpeg2video | mpeg2_cuvid | MPEG-2 video | 102 | | mpeg4 | mpeg4_cuvid | MPEG-4 part 2 | 103 | | vc1 | vc1_cuvid | SMPTE VC-1 | 104 | | vp8 | vp8_cuvid | On2 VP8 | 105 | | vp9 | vp9_cuvid | Google VP9 | 106 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | services: 3 | hls: 4 | build: "./" 5 | container_name: "hls" 6 | restart: "always" 7 | tmpfs: 8 | - "/var/www/html:mode=777,size=128M,uid=1000,gid=1000" 9 | ports: 10 | - "80:80" 11 | environment: 12 | SOURCES: | 13 | ch1 rtsp://192.168.1.5:554/ch1 14 | ch2 rtsp://192.168.1.5:554/ch2 15 | ch1_hd http://192.168.1.6/stream/channelid/8967896?profile=pass 16 | ch2_hd http://192.168.1.6/stream/channelid/4969639?profile=pass 17 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function generate_entry { 4 | echo '' 5 | echo "[program:ffmpeg-$1]" 6 | echo "command=/usr/src/profiles/${PROFILE:=passthrough}.sh '$1' '$2'" 7 | echo 'autostart=true' 8 | echo 'autorestart=true' 9 | echo 'startretries=5' 10 | echo 'numprocs=1' 11 | echo 'startsecs=0' 12 | echo 'user=%(ENV_USER)s' 13 | echo 'process_name=%(program_name)s_%(process_num)02d' 14 | echo 'stderr_logfile=/var/log/supervisor/%(program_name)s_stderr.log' 15 | echo 'stderr_logfile_maxbytes=10MB' 16 | echo 'stdout_logfile=/var/log/supervisor/%(program_name)s_stdout.log' 17 | echo 'stdout_logfile_maxbytes=10MB' 18 | } >> /usr/src/supervisord.conf 19 | 20 | SAVEIFS=$IFS # Save current IFS 21 | IFS=$'\n' # Change IFS to new line 22 | SOURCES=($SOURCES) # split to array $SOURCES 23 | IFS=$SAVEIFS # Restore IFS 24 | 25 | for (( i=0; i<${#SOURCES[@]}; i++ )) 26 | do 27 | ROW=(${SOURCES[$i]}) 28 | NAME=${ROW[0]} 29 | URL=${ROW[1]} 30 | 31 | if [[ ${NAME:0:1} == "#" ]]; then 32 | continue 33 | fi 34 | 35 | echo "[$i] name: ${NAME}" 36 | echo "[$i] url: ${URL}" 37 | 38 | generate_entry "${NAME}" "${URL}" 39 | done 40 | 41 | cat /usr/src/supervisord.conf 42 | 43 | # 44 | # start supervisor 45 | /usr/bin/supervisord -c /usr/src/supervisord.conf 46 | 47 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | root /var/www/html/; 4 | index index.html; 5 | 6 | location / { 7 | add_header 'Access-Control-Allow-Origin' '*'; 8 | add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS'; 9 | add_header 'Access-Control-Allow-Headers' 'Origin,Keep-Alive,Cache-Control,Content-Type,Accept,Range'; 10 | 11 | if ($request_method = 'OPTIONS') { 12 | add_header 'Access-Control-Max-Age' 3600; 13 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 14 | add_header 'Content-Length' 0; 15 | return 204; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Player 6 | 7 | 8 | 9 | 10 |

Player

11 |
12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /profiles/abr_transcoding_hd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function generate_playlist { 4 | echo '#EXTM3U' 5 | echo '#EXT-X-VERSION:3' 6 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 7 | echo "$1_360p.m3u8" 8 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 9 | echo "$1_480p.m3u8" 10 | echo '#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720' 11 | echo "$1_720p.m3u8" 12 | } > "/var/www/html/$1.m3u8" 13 | generate_playlist "$1" 14 | 15 | ffmpeg -hide_banner \ 16 | -i "$2" \ 17 | -map 0:v:0 -map 0:a:0 \ 18 | -vf scale=w=640:h=360:force_original_aspect_ratio=decrease \ 19 | -c:a aac \ 20 | -ar 48000 \ 21 | -b:a 96k \ 22 | -c:v h264 \ 23 | -profile:v main \ 24 | -b:v 800k \ 25 | -maxrate 856k \ 26 | -bufsize 1200k \ 27 | -crf 20 \ 28 | -sc_threshold 0 \ 29 | -g 48 \ 30 | -keyint_min 48 \ 31 | -f hls \ 32 | -hls_time 6 \ 33 | -hls_list_size 4 \ 34 | -hls_delete_threshold 1 \ 35 | -hls_flags delete_segments \ 36 | -hls_start_number_source datetime \ 37 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 38 | "/var/www/html/$1_360p.m3u8" \ 39 | -map 0:v:0 -map 0:a:0 \ 40 | -vf scale=w=842:h=480:force_original_aspect_ratio=decrease \ 41 | -c:a aac \ 42 | -ar 48000 \ 43 | -b:a 128k \ 44 | -c:v h264 \ 45 | -profile:v main \ 46 | -b:v 1400k \ 47 | -maxrate 1498k \ 48 | -bufsize 2100k \ 49 | -crf 20 \ 50 | -sc_threshold 0 \ 51 | -g 48 \ 52 | -keyint_min 48 \ 53 | -f hls \ 54 | -hls_time 6 \ 55 | -hls_list_size 4 \ 56 | -hls_delete_threshold 1 \ 57 | -hls_flags delete_segments \ 58 | -hls_start_number_source datetime \ 59 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 60 | "/var/www/html/$1_480p.m3u8" \ 61 | -map 0:v:0 -map 0:a:0 \ 62 | -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease \ 63 | -c:a aac \ 64 | -ar 48000 \ 65 | -b:a 128k \ 66 | -c:v h264 \ 67 | -profile:v main \ 68 | -b:v 2800k \ 69 | -maxrate 2996k \ 70 | -bufsize 4200k \ 71 | -crf 20 \ 72 | -sc_threshold 0 \ 73 | -g 48 \ 74 | -keyint_min 48 \ 75 | -f hls \ 76 | -hls_time 6 \ 77 | -hls_list_size 4 \ 78 | -hls_delete_threshold 1 \ 79 | -hls_flags delete_segments \ 80 | -hls_start_number_source datetime \ 81 | -hls_segment_filename "/var/www/html/$1_720p_%03d.ts" \ 82 | "/var/www/html/$1_720p.m3u8"; 83 | -------------------------------------------------------------------------------- /profiles/abr_transcoding_hd_1080p.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function generate_playlist { 4 | echo '#EXTM3U' 5 | echo '#EXT-X-VERSION:3' 6 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 7 | echo "$1_360p.m3u8" 8 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 9 | echo "$1_480p.m3u8" 10 | echo '#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720' 11 | echo "$1_720p.m3u8" 12 | echo '#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080' 13 | echo "$1_1080p.m3u8" 14 | } > "/var/www/html/$1.m3u8" 15 | generate_playlist "$1" 16 | 17 | ffmpeg -hide_banner \ 18 | -i "$2" \ 19 | -map 0:v:0 -map 0:a:0 \ 20 | -vf scale=w=640:h=360:force_original_aspect_ratio=decrease \ 21 | -c:a aac \ 22 | -ar 48000 \ 23 | -b:a 96k \ 24 | -c:v h264 \ 25 | -profile:v main \ 26 | -b:v 800k \ 27 | -maxrate 856k \ 28 | -bufsize 1200k \ 29 | -crf 20 \ 30 | -sc_threshold 0 \ 31 | -g 48 \ 32 | -keyint_min 48 \ 33 | -f hls \ 34 | -hls_time 6 \ 35 | -hls_list_size 4 \ 36 | -hls_delete_threshold 1 \ 37 | -hls_flags delete_segments \ 38 | -hls_start_number_source datetime \ 39 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 40 | "/var/www/html/$1_360p.m3u8" \ 41 | -map 0:v:0 -map 0:a:0 \ 42 | -vf scale=w=842:h=480:force_original_aspect_ratio=decrease \ 43 | -c:a aac \ 44 | -ar 48000 \ 45 | -b:a 128k \ 46 | -c:v h264 \ 47 | -profile:v main \ 48 | -b:v 1400k \ 49 | -maxrate 1498k \ 50 | -bufsize 2100k \ 51 | -crf 20 \ 52 | -sc_threshold 0 \ 53 | -g 48 \ 54 | -keyint_min 48 \ 55 | -f hls \ 56 | -hls_time 6 \ 57 | -hls_list_size 4 \ 58 | -hls_delete_threshold 1 \ 59 | -hls_flags delete_segments \ 60 | -hls_start_number_source datetime \ 61 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 62 | "/var/www/html/$1_480p.m3u8" \ 63 | -map 0:v:0 -map 0:a:0 \ 64 | -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease \ 65 | -c:a aac \ 66 | -ar 48000 \ 67 | -b:a 128k \ 68 | -c:v h264 \ 69 | -profile:v main \ 70 | -b:v 2800k \ 71 | -maxrate 2996k \ 72 | -bufsize 4200k \ 73 | -crf 20 \ 74 | -sc_threshold 0 \ 75 | -g 48 \ 76 | -keyint_min 48 \ 77 | -f hls \ 78 | -hls_time 6 \ 79 | -hls_list_size 4 \ 80 | -hls_delete_threshold 1 \ 81 | -hls_flags delete_segments \ 82 | -hls_start_number_source datetime \ 83 | -hls_segment_filename "/var/www/html/$1_720p_%03d.ts" \ 84 | "/var/www/html/$1_720p.m3u8" \ 85 | -map 0:v:0 -map 0:a:0 \ 86 | -vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease \ 87 | -c:a aac \ 88 | -ar 48000 \ 89 | -b:a 192k \ 90 | -c:v h264 \ 91 | -profile:v main \ 92 | -b:v 5000k \ 93 | -maxrate 5350k \ 94 | -bufsize 7500k \ 95 | -crf 20 \ 96 | -sc_threshold 0 \ 97 | -g 48 \ 98 | -keyint_min 48 \ 99 | -f hls \ 100 | -hls_time 6 \ 101 | -hls_list_size 4 \ 102 | -hls_delete_threshold 1 \ 103 | -hls_flags delete_segments \ 104 | -hls_start_number_source datetime \ 105 | -hls_segment_filename "/var/www/html/$1_1080p_%03d.ts" \ 106 | "/var/www/html/$1_1080p.m3u8"; 107 | -------------------------------------------------------------------------------- /profiles/abr_transcoding_sd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function generate_playlist { 4 | echo '#EXTM3U' 5 | echo '#EXT-X-VERSION:3' 6 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 7 | echo "$1_360p.m3u8" 8 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 9 | echo "$1_480p.m3u8" 10 | } > "/var/www/html/$1.m3u8" 11 | generate_playlist "$1" 12 | 13 | ffmpeg -hide_banner \ 14 | -i "$2" \ 15 | -map 0:v:0 -map 0:a:0 \ 16 | -vf scale=w=640:h=360:force_original_aspect_ratio=decrease \ 17 | -c:a aac \ 18 | -ar 48000 \ 19 | -b:a 96k \ 20 | -c:v h264 \ 21 | -profile:v main \ 22 | -b:v 800k \ 23 | -maxrate 856k \ 24 | -bufsize 1200k \ 25 | -crf 20 \ 26 | -sc_threshold 0 \ 27 | -g 48 \ 28 | -keyint_min 48 \ 29 | -f hls \ 30 | -hls_time 6 \ 31 | -hls_list_size 4 \ 32 | -hls_delete_threshold 1 \ 33 | -hls_flags delete_segments \ 34 | -hls_start_number_source datetime \ 35 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 36 | "/var/www/html/$1_360p.m3u8" \ 37 | -map 0:v:0 -map 0:a:0 \ 38 | -vf scale=w=842:h=480:force_original_aspect_ratio=decrease \ 39 | -c:a aac \ 40 | -ar 48000 \ 41 | -b:a 128k \ 42 | -c:v h264 \ 43 | -profile:v main \ 44 | -b:v 1400k \ 45 | -maxrate 1498k \ 46 | -bufsize 2100k \ 47 | -crf 20 \ 48 | -sc_threshold 0 \ 49 | -g 48 \ 50 | -keyint_min 48 \ 51 | -f hls \ 52 | -hls_time 6 \ 53 | -hls_list_size 4 \ 54 | -hls_delete_threshold 1 \ 55 | -hls_flags delete_segments \ 56 | -hls_start_number_source datetime \ 57 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 58 | "/var/www/html/$1_480p.m3u8"; 59 | -------------------------------------------------------------------------------- /profiles/passthrough.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ffmpeg -hide_banner \ 3 | -i "$2" \ 4 | -map 0:v:0 -map 0:a:0 \ 5 | -c:v copy \ 6 | -c:a copy \ 7 | -f hls \ 8 | -hls_time 6 \ 9 | -hls_list_size 4 \ 10 | -hls_delete_threshold 1 \ 11 | -hls_flags delete_segments \ 12 | -hls_start_number_source datetime \ 13 | "/var/www/html/$1.m3u8"; 14 | -------------------------------------------------------------------------------- /profiles/transcoding_hd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ffmpeg -hide_banner \ 3 | -i "$2" \ 4 | -map 0:v:0 -map 0:a:0 \ 5 | -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease \ 6 | -c:a aac \ 7 | -ar 48000 \ 8 | -b:a 128k \ 9 | -c:v h264 \ 10 | -profile:v main \ 11 | -b:v 2800k \ 12 | -maxrate 2996k \ 13 | -bufsize 4200k \ 14 | -crf 20 \ 15 | -sc_threshold 0 \ 16 | -g 48 \ 17 | -keyint_min 48 \ 18 | -f hls \ 19 | -hls_time 6 \ 20 | -hls_list_size 4 \ 21 | -hls_delete_threshold 1 \ 22 | -hls_flags delete_segments \ 23 | -hls_start_number_source datetime \ 24 | -hls_segment_filename "/var/www/html/$1_%03d.ts" \ 25 | "/var/www/html/$1.m3u8"; 26 | -------------------------------------------------------------------------------- /profiles/transcoding_sd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ffmpeg -hide_banner \ 3 | -i "$2" \ 4 | -map 0:v:0 -map 0:a:0 \ 5 | -vf scale=w=842:h=480:force_original_aspect_ratio=decrease \ 6 | -c:a aac \ 7 | -ar 48000 \ 8 | -b:a 128k \ 9 | -c:v h264 \ 10 | -profile:v main \ 11 | -b:v 1400k \ 12 | -maxrate 1498k \ 13 | -bufsize 2100k \ 14 | -crf 20 \ 15 | -sc_threshold 0 \ 16 | -g 48 \ 17 | -keyint_min 48 \ 18 | -f hls \ 19 | -hls_time 6 \ 20 | -hls_list_size 4 \ 21 | -hls_delete_threshold 1 \ 22 | -hls_flags delete_segments \ 23 | -hls_start_number_source datetime \ 24 | -hls_segment_filename "/var/www/html/$1_%03d.ts" \ 25 | "/var/www/html/$1.m3u8"; 26 | -------------------------------------------------------------------------------- /profiles_nvidia/.helpers.sh: -------------------------------------------------------------------------------- 1 | function cuvid_codec { 2 | SUPPORTED_CODECS="h264 hevc mjpeg mpeg1video mpeg2video mpeg4 vc1 vp8 vp9" 3 | CODEC=$(ffprobe -hide_banner -loglevel panic -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "$1" | head -1) 4 | 5 | if [[ ! " ${SUPPORTED_CODECS} " =~ " ${CODEC} " ]]; then 6 | echo "Unsupported codec ${CODEC}." 7 | return 1 8 | fi 9 | 10 | if [[ "${CODEC}" == "mpeg1video" ]]; then 11 | echo "mpeg1_cuvid" 12 | return 0 13 | fi 14 | 15 | if [[ "${CODEC}" == "mpeg2video" ]]; then 16 | echo "mpeg2_cuvid" 17 | return 0 18 | fi 19 | 20 | echo "${CODEC}_cuvid" 21 | } 22 | -------------------------------------------------------------------------------- /profiles_nvidia/abr_transcoding_hd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | source .helpers.sh 5 | 6 | function generate_playlist { 7 | echo '#EXTM3U' 8 | echo '#EXT-X-VERSION:3' 9 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 10 | echo "$1_360p.m3u8" 11 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 12 | echo "$1_480p.m3u8" 13 | echo '#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720' 14 | echo "$1_720p.m3u8" 15 | } > "/var/www/html/$1.m3u8" 16 | generate_playlist "$1" 17 | 18 | ffmpeg -hide_banner -hwaccel_output_format cuda -c:v "$(cuvid_codec "$2")" \ 19 | -i "$2" \ 20 | -map 0:v:0 -map 0:a:0 \ 21 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=640:360:interp_algo=super \ 22 | -c:a aac \ 23 | -ar 48000 \ 24 | -b:a 96k \ 25 | -c:v h264_nvenc \ 26 | -profile:v main \ 27 | -b:v 800k \ 28 | -maxrate 856k \ 29 | -bufsize 1200k \ 30 | -crf 20 \ 31 | -sc_threshold 0 \ 32 | -g 48 \ 33 | -keyint_min 48 \ 34 | -f hls \ 35 | -hls_time 6 \ 36 | -hls_list_size 4 \ 37 | -hls_delete_threshold 1 \ 38 | -hls_flags delete_segments \ 39 | -hls_start_number_source datetime \ 40 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 41 | "/var/www/html/$1_360p.m3u8" \ 42 | -map 0:v:0 -map 0:a:0 \ 43 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=842:480:interp_algo=super \ 44 | -c:a aac \ 45 | -ar 48000 \ 46 | -b:a 128k \ 47 | -c:v h264_nvenc \ 48 | -profile:v main \ 49 | -b:v 1400k \ 50 | -maxrate 1498k \ 51 | -bufsize 2100k \ 52 | -crf 20 \ 53 | -sc_threshold 0 \ 54 | -g 48 \ 55 | -keyint_min 48 \ 56 | -f hls \ 57 | -hls_time 6 \ 58 | -hls_list_size 4 \ 59 | -hls_delete_threshold 1 \ 60 | -hls_flags delete_segments \ 61 | -hls_start_number_source datetime \ 62 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 63 | "/var/www/html/$1_480p.m3u8" \ 64 | -map 0:v:0 -map 0:a:0 \ 65 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=1280:720:interp_algo=super \ 66 | -c:a aac \ 67 | -ar 48000 \ 68 | -b:a 128k \ 69 | -c:v h264_nvenc \ 70 | -profile:v main \ 71 | -b:v 2800k \ 72 | -maxrate 2996k \ 73 | -bufsize 4200k \ 74 | -crf 20 \ 75 | -sc_threshold 0 \ 76 | -g 48 \ 77 | -keyint_min 48 \ 78 | -f hls \ 79 | -hls_time 6 \ 80 | -hls_list_size 4 \ 81 | -hls_delete_threshold 1 \ 82 | -hls_flags delete_segments \ 83 | -hls_start_number_source datetime \ 84 | -hls_segment_filename "/var/www/html/$1_720p_%03d.ts" \ 85 | "/var/www/html/$1_720p.m3u8"; 86 | -------------------------------------------------------------------------------- /profiles_nvidia/abr_transcoding_hd_1080p.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | source .helpers.sh 5 | 6 | function generate_playlist { 7 | echo '#EXTM3U' 8 | echo '#EXT-X-VERSION:3' 9 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 10 | echo "$1_360p.m3u8" 11 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 12 | echo "$1_480p.m3u8" 13 | echo '#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720' 14 | echo "$1_720p.m3u8" 15 | echo '#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080' 16 | echo "$1_1080p.m3u8" 17 | } > "/var/www/html/$1.m3u8" 18 | generate_playlist "$1" 19 | 20 | ffmpeg -hide_banner -hwaccel_output_format cuda -c:v "$(cuvid_codec "$2")" \ 21 | -i "$2" \ 22 | -map 0:v:0 -map 0:a:0 \ 23 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=640:360:interp_algo=super \ 24 | -c:a aac \ 25 | -ar 48000 \ 26 | -b:a 96k \ 27 | -c:v h264_nvenc \ 28 | -profile:v main \ 29 | -b:v 800k \ 30 | -maxrate 856k \ 31 | -bufsize 1200k \ 32 | -crf 20 \ 33 | -sc_threshold 0 \ 34 | -g 48 \ 35 | -keyint_min 48 \ 36 | -f hls \ 37 | -hls_time 6 \ 38 | -hls_list_size 4 \ 39 | -hls_delete_threshold 1 \ 40 | -hls_flags delete_segments \ 41 | -hls_start_number_source datetime \ 42 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 43 | "/var/www/html/$1_360p.m3u8" \ 44 | -map 0:v:0 -map 0:a:0 \ 45 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=842:480:interp_algo=super \ 46 | -c:a aac \ 47 | -ar 48000 \ 48 | -b:a 128k \ 49 | -c:v h264_nvenc \ 50 | -profile:v main \ 51 | -b:v 1400k \ 52 | -maxrate 1498k \ 53 | -bufsize 2100k \ 54 | -crf 20 \ 55 | -sc_threshold 0 \ 56 | -g 48 \ 57 | -keyint_min 48 \ 58 | -f hls \ 59 | -hls_time 6 \ 60 | -hls_list_size 4 \ 61 | -hls_delete_threshold 1 \ 62 | -hls_flags delete_segments \ 63 | -hls_start_number_source datetime \ 64 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 65 | "/var/www/html/$1_480p.m3u8" \ 66 | -map 0:v:0 -map 0:a:0 \ 67 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=1280:720:interp_algo=super \ 68 | -c:a aac \ 69 | -ar 48000 \ 70 | -b:a 128k \ 71 | -c:v h264_nvenc \ 72 | -profile:v main \ 73 | -b:v 2800k \ 74 | -maxrate 2996k \ 75 | -bufsize 4200k \ 76 | -crf 20 \ 77 | -sc_threshold 0 \ 78 | -g 48 \ 79 | -keyint_min 48 \ 80 | -f hls \ 81 | -hls_time 6 \ 82 | -hls_list_size 4 \ 83 | -hls_delete_threshold 1 \ 84 | -hls_flags delete_segments \ 85 | -hls_start_number_source datetime \ 86 | -hls_segment_filename "/var/www/html/$1_720p_%03d.ts" \ 87 | "/var/www/html/$1_720p.m3u8" \ 88 | -map 0:v:0 -map 0:a:0 \ 89 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=1920:1080:interp_algo=super \ 90 | -c:a aac \ 91 | -ar 48000 \ 92 | -b:a 192k \ 93 | -c:v h264_nvenc \ 94 | -profile:v main \ 95 | -b:v 5000k \ 96 | -maxrate 5350k \ 97 | -bufsize 7500k \ 98 | -crf 20 \ 99 | -sc_threshold 0 \ 100 | -g 48 \ 101 | -keyint_min 48 \ 102 | -f hls \ 103 | -hls_time 6 \ 104 | -hls_list_size 4 \ 105 | -hls_delete_threshold 1 \ 106 | -hls_flags delete_segments \ 107 | -hls_start_number_source datetime \ 108 | -hls_segment_filename "/var/www/html/$1_1080p_%03d.ts" \ 109 | "/var/www/html/$1_1080p.m3u8"; 110 | -------------------------------------------------------------------------------- /profiles_nvidia/abr_transcoding_sd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | source .helpers.sh 5 | 6 | function generate_playlist { 7 | echo '#EXTM3U' 8 | echo '#EXT-X-VERSION:3' 9 | echo '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360' 10 | echo "$1_360p.m3u8" 11 | echo '#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480' 12 | echo "$1_480p.m3u8" 13 | } > "/var/www/html/$1.m3u8" 14 | generate_playlist "$1" 15 | 16 | ffmpeg -hide_banner -hwaccel_output_format cuda -c:v "$(cuvid_codec "$2")" \ 17 | -i "$2" \ 18 | -map 0:v:0 -map 0:a:0 \ 19 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=640:360:interp_algo=super \ 20 | -c:a aac \ 21 | -ar 48000 \ 22 | -b:a 96k \ 23 | -c:v h264_nvenc \ 24 | -profile:v main \ 25 | -b:v 800k \ 26 | -maxrate 856k \ 27 | -bufsize 1200k \ 28 | -crf 20 \ 29 | -sc_threshold 0 \ 30 | -g 48 \ 31 | -keyint_min 48 \ 32 | -f hls \ 33 | -hls_time 6 \ 34 | -hls_list_size 4 \ 35 | -hls_delete_threshold 1 \ 36 | -hls_flags delete_segments \ 37 | -hls_start_number_source datetime \ 38 | -hls_segment_filename "/var/www/html/$1_360p_%03d.ts" \ 39 | "/var/www/html/$1_360p.m3u8" \ 40 | -map 0:v:0 -map 0:a:0 \ 41 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=842:480:interp_algo=super \ 42 | -c:a aac \ 43 | -ar 48000 \ 44 | -b:a 128k \ 45 | -c:v h264_nvenc \ 46 | -profile:v main \ 47 | -b:v 1400k \ 48 | -maxrate 1498k \ 49 | -bufsize 2100k \ 50 | -crf 20 \ 51 | -sc_threshold 0 \ 52 | -g 48 \ 53 | -keyint_min 48 \ 54 | -f hls \ 55 | -hls_time 6 \ 56 | -hls_list_size 4 \ 57 | -hls_delete_threshold 1 \ 58 | -hls_flags delete_segments \ 59 | -hls_start_number_source datetime \ 60 | -hls_segment_filename "/var/www/html/$1_480p_%03d.ts" \ 61 | "/var/www/html/$1_480p.m3u8"; 62 | -------------------------------------------------------------------------------- /profiles_nvidia/passthrough.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ffmpeg -hide_banner \ 3 | -i "$2" \ 4 | -map 0:v:0 -map 0:a:0 \ 5 | -c:v copy \ 6 | -c:a copy \ 7 | -f hls \ 8 | -hls_time 6 \ 9 | -hls_list_size 4 \ 10 | -hls_delete_threshold 1 \ 11 | -hls_flags delete_segments \ 12 | -hls_start_number_source datetime \ 13 | "/var/www/html/$1.m3u8"; 14 | 15 | -------------------------------------------------------------------------------- /profiles_nvidia/transcoding_hd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | source .helpers.sh 5 | 6 | ffmpeg -hide_banner -hwaccel_output_format cuda -c:v "$(cuvid_codec "$2")" \ 7 | -i "$2" \ 8 | -map 0:v:0 -map 0:a:0 \ 9 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=1280:720:interp_algo=super \ 10 | -c:a aac \ 11 | -ar 48000 \ 12 | -b:a 128k \ 13 | -c:v h264_nvenc \ 14 | -profile:v main \ 15 | -b:v 2800k \ 16 | -maxrate 2996k \ 17 | -bufsize 4200k \ 18 | -crf 20 \ 19 | -sc_threshold 0 \ 20 | -g 48 \ 21 | -keyint_min 48 \ 22 | -f hls \ 23 | -hls_time 6 \ 24 | -hls_list_size 4 \ 25 | -hls_delete_threshold 1 \ 26 | -hls_flags delete_segments \ 27 | -hls_start_number_source datetime \ 28 | -hls_segment_filename "/var/www/html/$1_%03d.ts" \ 29 | "/var/www/html/$1.m3u8"; 30 | -------------------------------------------------------------------------------- /profiles_nvidia/transcoding_sd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | source .helpers.sh 5 | 6 | ffmpeg -hide_banner -hwaccel_output_format cuda -c:v "$(cuvid_codec "$2")" \ 7 | -i "$2" \ 8 | -map 0:v:0 -map 0:a:0 \ 9 | -vf hwupload_cuda,yadif_cuda=0:-1:0,scale_npp=842:480:interp_algo=super \ 10 | -c:a aac \ 11 | -ar 48000 \ 12 | -b:a 128k \ 13 | -c:v h264_nvenc \ 14 | -profile:v main \ 15 | -b:v 1400k \ 16 | -maxrate 1498k \ 17 | -bufsize 2100k \ 18 | -crf 20 \ 19 | -sc_threshold 0 \ 20 | -g 48 \ 21 | -keyint_min 48 \ 22 | -f hls \ 23 | -hls_time 6 \ 24 | -hls_list_size 4 \ 25 | -hls_delete_threshold 1 \ 26 | -hls_flags delete_segments \ 27 | -hls_start_number_source datetime \ 28 | -hls_segment_filename "/var/www/html/$1_%03d.ts" \ 29 | "/var/www/html/$1.m3u8"; 30 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | pidfile=/var/run/supervisord.pid 4 | logfile=/dev/null 5 | logfile_maxbytes=0 6 | loglevel=debug 7 | 8 | [program:nginx] 9 | command=/usr/sbin/nginx -g "daemon off;" 10 | autostart=true 11 | autorestart=true 12 | startretries=5 13 | numprocs=1 14 | startsecs=0 15 | process_name=%(program_name)s_%(process_num)02d 16 | stderr_logfile=/var/log/supervisor/%(program_name)s_stderr.log 17 | stderr_logfile_maxbytes=10MB 18 | stdout_logfile=/var/log/supervisor/%(program_name)s_stdout.log 19 | stdout_logfile_maxbytes=10MB 20 | 21 | --------------------------------------------------------------------------------