├── .dockerignore ├── .github └── workflows │ ├── docker.yml │ └── pages.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── bin └── app.js ├── data ├── hls │ └── .gitkeep ├── media │ └── .gitkeep ├── playlist │ └── .gitkeep ├── qr │ └── .gitkeep ├── thumbnail │ └── .gitkeep └── vmaf │ └── .gitkeep ├── docker-compose.yml ├── docs ├── Gemfile ├── Gemfile.lock ├── _config.yml ├── assets │ ├── Blackmagic DeckLink SDK.pdf │ ├── api-spec.yml │ └── css │ │ └── style.scss ├── index.md └── pages │ ├── decklink │ └── index.md │ ├── installation │ ├── building.md │ ├── decklink.md │ ├── environment.md │ ├── index.md │ ├── ndi.md │ └── prebuilt.md │ ├── ndi │ └── index.md │ ├── usage │ ├── endpoints.md │ └── index.md │ └── vmaf │ └── index.md ├── index.js ├── logs └── .gitkeep ├── package-lock.json ├── package.json ├── public ├── fonts │ ├── short-baby.ttf │ └── swansea-bold.ttf ├── images │ └── favicon.ico └── js │ ├── chart.js │ ├── clock.js │ ├── index.js │ ├── jobs.js │ └── video.js ├── routes ├── audio.js ├── auth.js ├── bars.js ├── decklink.js ├── file.js ├── hls.js ├── page.js ├── playlist.js ├── rtp.js ├── srt.js ├── system.js ├── udp.js └── vmaf.js ├── services ├── audio-decklink.js ├── audio-hls.js ├── audio-rtmp.js ├── audio-rtp.js ├── audio-srt.js ├── audio-udp.js ├── bars-decklink.js ├── bars-file.js ├── bars-hls.js ├── bars-rtmp.js ├── bars-rtp.js ├── bars-srt.js ├── bars-udp.js ├── decklink-config-get.js ├── decklink-config-set.js ├── decklink-file.js ├── decklink-hls.js ├── decklink-rtmp.js ├── decklink-rtp.js ├── decklink-srt.js ├── decklink-udp.js ├── file-decklink.js ├── file-hls.js ├── file-list.js ├── file-metadata.js ├── file-rtmp.js ├── file-rtp.js ├── file-srt.js ├── file-udp.js ├── rtp-decklink.js ├── rtp-file.js ├── srt-decklink.js ├── srt-file.js ├── system-info.js ├── system-job-get.js ├── system-job-getall.js ├── system-job-getthumbnail.js ├── system-job-kill.js ├── system-job-killall.js ├── system-stats.js ├── system-time-set.js ├── udp-decklink.js ├── udp-file.js ├── vmaf-file-test.js ├── vmaf-models-get.js ├── vmaf-results-csv.js ├── vmaf-results-json.js ├── zz-decklink-pause.js └── zz-decklink-stop.js ├── setup.sh ├── utils ├── barsTypes.js ├── decklink-version.js ├── decklinkFormats.js ├── documentation.js ├── encodePresets.js ├── ffconcat.js ├── ffmpeg-getcodecs.js ├── file-delete.js ├── file-exists.js ├── file-read-json.js ├── file-read.js ├── file-upload.js ├── filenames-get.js ├── filter-audio-meter.js ├── filter-combine.js ├── filter-image.js ├── filter-interlace.js ├── filter-qr.js ├── filter-text.js ├── get-extension.js ├── get-filepath.js ├── git-commit.js ├── hash-response.js ├── http-logger.js ├── job-load.js ├── jobManager.js ├── logger.js ├── markdown.js ├── node-version.js ├── os-version.js ├── parse.js ├── qr.js ├── rtmp-address.js ├── set-codec.js ├── strategies.js ├── thumbnail-cache.js ├── validator-isiporfqdn.js ├── vmaf-json-csv.js └── watcher.js ├── validators ├── audio.js ├── auth.js ├── bars.js ├── decklink.js ├── file.js ├── hls.js ├── overlay.js ├── rtmp.js ├── rtp.js ├── srt.js ├── thumbnail.js ├── udp.js └── vmaf.js └── views ├── chart.html ├── clock.html ├── index.html ├── jobs.html ├── partials ├── footer.html ├── head.html └── header.html └── video.html /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/docker.yml 2 | name: Docker Deploy 3 | 4 | env: 5 | regsitry: "ghcr.io" 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | tags: 12 | - "v*" 13 | paths-ignore: 14 | - "**/README.md" 15 | - "docs/**" 16 | 17 | jobs: 18 | docker: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | matrix: 22 | architecture: [linux/amd64] 23 | defaults: 24 | run: 25 | working-directory: ./ 26 | steps: 27 | #Get the latest version of the repository 28 | - name: Checkout 29 | uses: actions/checkout@v3 30 | - uses: actions/setup-node@v3 31 | #Get Current and DateTime for Build 32 | - name: Set current date and time as an environment variable 33 | run: echo "timestamp=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV 34 | - name: Print current date and time 35 | run: echo ${{ env.timestamp }} 36 | #Get the package.json version 37 | - uses: martinbeentjes/npm-get-version-action@master 38 | name: Get Version Number 39 | id: version 40 | with: 41 | path: ./ 42 | - name: Print Current Version 43 | run: echo Building with version ${{ steps.version.outputs.current-version }} 44 | #Docker Buildx Setup 45 | - name: Set up QEMU 46 | uses: docker/setup-qemu-action@v2 47 | - name: Set up Docker Buildx 48 | id: buildx 49 | uses: docker/setup-buildx-action@v2 50 | with: 51 | buildkitd-flags: --debug 52 | - name: List available platforms 53 | run: echo ${{ steps.buildx.outputs.platforms }} 54 | #Docker Registry Login 55 | - name: Login to GHCR Registry 56 | uses: docker/login-action@v2 57 | with: 58 | registry: ${{ env.regsitry }} 59 | username: ${{ secrets.GH_USER }} 60 | password: ${{ secrets.GH_TOKEN }} 61 | logout: false 62 | #Docker Build Image and Push to Registry 63 | - name: Build and push to registry 64 | uses: docker/build-push-action@v3 65 | with: 66 | context: "." 67 | platforms: ${{ matrix.architecture}} 68 | push: ${{ github.event_name != 'pull_request' }} 69 | tags: | 70 | ${{env.regsitry}}/ryanmccartney/ffmpeg-docker:latest 71 | ${{env.regsitry}}/ryanmccartney/ffmpeg-docker:${{steps.version.outputs.current-version}} 72 | outputs: type=registry 73 | build-args: | 74 | DECKLINK_SUPPORT="false" 75 | NDI_SUPPORT="false" 76 | NON_FREE="false" 77 | labels: | 78 | author=${{github.actor}} 79 | version=${{steps.version.outputs.current-version}} 80 | org.opencontainers.image.licenses=Apache-2.0 81 | org.opencontainers.image.source=${{github.server_url}}/${{github.repository}} 82 | info.mccartney.ryan.build.timestamp=${{env.timestamp}} 83 | info.mccartney.ryan.build.number=${{github.run_number}} 84 | info.mccartney.ryan.build.id=${{github.run_id}} 85 | info.mccartney.ryan.build.branch=${{github.ref_name}} 86 | info.mccartney.ryan.build.commit=${{github.sha}} 87 | info.mccartney.ryan.build.repository=${{github.server_url}}/${{github.repository}} 88 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Pages Deploy 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Build job 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v3 30 | - name: Setup Pages 31 | uses: actions/configure-pages@v2 32 | - name: Build with Jekyll 33 | uses: actions/jekyll-build-pages@v1 34 | with: 35 | source: /docs 36 | destination: ./_site 37 | - name: Upload artifact 38 | uses: actions/upload-pages-artifact@v1 39 | 40 | # Deployment job 41 | deploy: 42 | environment: 43 | name: github-pages 44 | url: ${{ steps.deployment.outputs.page_url }} 45 | runs-on: ubuntu-latest 46 | needs: build 47 | steps: 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v1 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | 4 | # Exclude FFmpeg logs 5 | /*.temp 6 | /*.log 7 | 8 | # Logs 9 | /logs/* 10 | !/logs/.gitkeep 11 | 12 | # Media 13 | /data/media/* 14 | !/data/media/.gitkeep 15 | 16 | # Playlist 17 | /data/playlist/* 18 | !/data/playlist/.gitkeep 19 | 20 | # Thumbnail 21 | /data/thumbnail/* 22 | !/data/thumbnail/.gitkeep 23 | 24 | # VMAF 25 | /data/vmaf/* 26 | !/data/vmaf/.gitkeep 27 | 28 | # HLS 29 | /data/hls/* 30 | !/data/hls/.gitkeep 31 | 32 | # QR 33 | /data/qr/* 34 | !/data/qr/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | FFmpeg in Docker 3 |

4 | 5 |

6 | Confiugrable FFmpeg in a docker container with a simple RESTful API 7 |

8 | 9 |

10 | 11 | continuous integration 12 | 13 | 14 | 15 | continuous integration 16 | 17 | 18 | 19 | contributors 20 | 21 | 22 | license 23 | 24 |

25 | 26 | FFmpeg compilation made easy with additional libraries and runs inside a Docker container. A RESTful API runs alongside written in Node.js utilising `express` and `fluent-ffmpeg`. 27 | 28 | For more information read the [documentation](https://ryan.mccartney.info/ffmpeg-docker/). 29 | 30 | :exclamation: This project is a work in progress, it is still in the early stages of development and functions may not work as expected. 31 | 32 | ## Pre-built Image 33 | 34 | You can use the pre-built image straight away - however, this is contains pre-compiled FFmpeg as a result it does not have the following options enabled to comply with FFmpeg licensing; 35 | 36 | - `--enable-nonfree` 37 | - `--enable-libndi_newtek` 38 | - `--enable-decklink` 39 | - `--enable-libsrt` 40 | - `--disable-libaom` 41 | - `--disable-libsvtav1` 42 | - `--enable-libklvanc` 43 | - `--enable-libvmaf` 44 | - `--enable-libfdk-aac` 45 | 46 | If this isn't a problem then you can get the image here; 47 | 48 | `docker pull ghcr.io/ryanmccartney/ffmpeg-docker:latest` 49 | 50 | ## Building 51 | 52 | If you want to contribute to this project or you'd like some of the above features, you'll need to build a copy of this image yourself which will compile FFmpeg locally. 53 | 54 | 1. Clone the repository `git clone https://github.com/ryanmccartney/ffmpeg-docker` 55 | 2. Change directory to the repository `cd ./ffmpeg-docker` 56 | 3. Build the image using `docker compose build`. 57 | 58 | ### on an M1 Mac 59 | 60 | 1. Install your packages locally - `npm install --platform=linux --arch=arm64v8 sharp` 61 | 62 | ## About 63 | 64 | This software uses libraries from the FFmpeg project under the LGPLv2.1. 65 | 66 | This software uses code of [FFmpeg](http://ffmpeg.org) licensed under the [LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) and its source can be downloaded [here](https://github.com/ryanmccartney/ffmpeg-docker) 67 | -------------------------------------------------------------------------------- /data/hls/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/hls/.gitkeep -------------------------------------------------------------------------------- /data/media/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/media/.gitkeep -------------------------------------------------------------------------------- /data/playlist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/playlist/.gitkeep -------------------------------------------------------------------------------- /data/qr/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/qr/.gitkeep -------------------------------------------------------------------------------- /data/thumbnail/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/thumbnail/.gitkeep -------------------------------------------------------------------------------- /data/vmaf/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/data/vmaf/.gitkeep -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | # Copyright (C) 2022 Ryan McCartney 4 | # 5 | # This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | # 7 | # FFmpeg Docker is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | version: "3.8" 22 | 23 | services: 24 | ffmpeg: 25 | container_name: ffmpeg 26 | # network_mode: "host" #Change this as required 27 | build: 28 | context: "." 29 | args: 30 | DECKLINK_SUPPORT: "false" 31 | DECKLINK_SDK_URL: "https://swr.cloud.blackmagicdesign.com/DeckLink/v12.4.1/Blackmagic_DeckLink_SDK_12.4.1.zip?verify=" 32 | DECKLINK_DRIVER_URL: "https://swr.cloud.blackmagicdesign.com/DesktopVideo/v12.4.1/Blackmagic_Desktop_Video_Linux_12.4.1.tar.gz?verify=" 33 | DECKLINK_DRIVER_VERSION: "12.4.1" 34 | NDI_SUPPORT: "false" 35 | NDI_SDK_URL: "https://downloads.ndi.tv/SDK/NDI_SDK_Linux/Install_NDI_SDK_v5_Linux.tar.gz" 36 | NON_FREE: "true" 37 | restart: always 38 | volumes: 39 | - ./:/home/node/app 40 | #devices: 41 | # - /dev/blackmagic/io0:/dev/blackmagic/io0 42 | environment: 43 | NODE_ENV: "development" 44 | PORT: 80 45 | HOST: "localhost" 46 | WEB_GUI: "true" 47 | AUTH_KEY: "averysecretkey" 48 | AUTH_USER: admin 49 | AUTH_PASSWORD: admin 50 | ports: 51 | - 80:80 52 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "jekyll", "~> 4.3" # installed by `gem jekyll` 4 | # gem "webrick" # required when using Ruby >= 3 and Jekyll <= 4.2.2 5 | 6 | gem "just-the-docs", "0.4.0.rc3" # currently the latest pre-release 7 | # gem "just-the-docs" # the latest release - currently 0.3.3 8 | -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.1) 5 | public_suffix (>= 2.0.2, < 6.0) 6 | colorator (1.1.0) 7 | concurrent-ruby (1.1.10) 8 | em-websocket (0.5.3) 9 | eventmachine (>= 0.12.9) 10 | http_parser.rb (~> 0) 11 | eventmachine (1.2.7) 12 | ffi (1.15.5) 13 | forwardable-extended (2.6.0) 14 | http_parser.rb (0.8.0) 15 | i18n (1.12.0) 16 | concurrent-ruby (~> 1.0) 17 | jekyll (4.3.0) 18 | addressable (~> 2.4) 19 | colorator (~> 1.0) 20 | em-websocket (~> 0.5) 21 | i18n (~> 1.0) 22 | jekyll-sass-converter (>= 2.0, < 4.0) 23 | jekyll-watch (~> 2.0) 24 | kramdown (~> 2.3, >= 2.3.1) 25 | kramdown-parser-gfm (~> 1.0) 26 | liquid (~> 4.0) 27 | mercenary (>= 0.3.6, < 0.5) 28 | pathutil (~> 0.9) 29 | rouge (>= 3.0, < 5.0) 30 | safe_yaml (~> 1.0) 31 | terminal-table (>= 1.8, < 4.0) 32 | webrick (~> 1.7) 33 | jekyll-sass-converter (2.2.0) 34 | sassc (> 2.0.1, < 3.0) 35 | jekyll-seo-tag (2.8.0) 36 | jekyll (>= 3.8, < 5.0) 37 | jekyll-watch (2.2.1) 38 | listen (~> 3.0) 39 | just-the-docs (0.4.0.rc3) 40 | jekyll (>= 3.8.5) 41 | jekyll-seo-tag (>= 2.0) 42 | rake (>= 12.3.1) 43 | kramdown (2.4.0) 44 | rexml 45 | kramdown-parser-gfm (1.1.0) 46 | kramdown (~> 2.0) 47 | liquid (4.0.3) 48 | listen (3.7.1) 49 | rb-fsevent (~> 0.10, >= 0.10.3) 50 | rb-inotify (~> 0.9, >= 0.9.10) 51 | mercenary (0.4.0) 52 | pathutil (0.16.2) 53 | forwardable-extended (~> 2.6) 54 | public_suffix (5.0.0) 55 | rake (13.0.6) 56 | rb-fsevent (0.11.2) 57 | rb-inotify (0.10.1) 58 | ffi (~> 1.0) 59 | rexml (3.2.5) 60 | rouge (4.0.0) 61 | safe_yaml (1.0.5) 62 | sassc (2.4.0) 63 | ffi (~> 1.9) 64 | terminal-table (3.0.2) 65 | unicode-display_width (>= 1.1.1, < 3) 66 | unicode-display_width (2.3.0) 67 | webrick (1.7.0) 68 | 69 | PLATFORMS 70 | arm64-darwin-21 71 | x86_64-darwin-19 72 | x86_64-linux 73 | 74 | DEPENDENCIES 75 | jekyll (~> 4.3) 76 | just-the-docs (= 0.4.0.rc3) 77 | 78 | BUNDLED WITH 79 | 2.3.9 80 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: Docker FFmpeg 2 | description: FFmpeg in Docker with a RESTful API for control 3 | remote_theme: pmarsceill/just-the-docs 4 | url: https://ryan.mccartney.info/ffmpeg-docker 5 | show_downloads: true 6 | -------------------------------------------------------------------------------- /docs/assets/Blackmagic DeckLink SDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/docs/assets/Blackmagic DeckLink SDK.pdf -------------------------------------------------------------------------------- /docs/assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | {% if site.color_scheme and site.color_scheme != "nil" %} 4 | {% assign color_scheme = site.color_scheme %} 5 | {% else %} 6 | {% assign color_scheme = "light" %} 7 | {% endif %} 8 | {% include css/just-the-docs.scss.liquid color_scheme=color_scheme %} 9 | 10 | a { 11 | color: #7EC77F; 12 | } 13 | 14 | .main-content { 15 | h1, 16 | h2, 17 | h3, 18 | h4, 19 | h5, 20 | h6 { 21 | color: #7EC77F; 22 | } 23 | } -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | layout: home 4 | nav_order: 1 5 | --- 6 | 7 | # FFmpeg in Docker 8 | 9 | [![Docker Deploy](https://github.com/ryanmccartney/ffmpeg-docker/actions/workflows/docker.yml/badge.svg)](https://github.com/ryanmccartney/ffmpeg-docker/actions/workflows/docker.yml) [![Pages Deploy](https://github.com/ryanmccartney/ffmpeg-docker/actions/workflows/pages.yml/badge.svg)](https://github.com/ryanmccartney/ffmpeg-docker/actions/workflows/pages.yml) 10 | 11 | FFmpeg compiled with additional libraries and running inside a Docker container. A RESTful API runs alongside written in Node.js utilsing `fluent-ffmpeg`. 12 | 13 | ## Exmaple 14 | 15 | `POST` comand to the following endpoint `http://localhost/api/bars/rtmp` with the JSON body 16 | 17 | ```json 18 | { 19 | "input": { 20 | "type": "smptehdbars" 21 | }, 22 | "output": { 23 | "address": "a.rtmp.youtube.com/live2", 24 | "key": "YOUTUBE_STREAM_KEY", 25 | "bitrate": "10M" 26 | }, 27 | "overlay": { 28 | "line1": "Test RTMP Stream", 29 | "line2": "FFmpeg in Docker", 30 | "font": "swansea-bold.ttf" 31 | }, 32 | "thumbnail": true 33 | } 34 | ``` 35 | 36 | This renders the following output. 37 | 38 | 39 | 40 | Try it with file inputs, decklink inputs and RIST, SRT or RTMP output. 41 | 42 | ## Pre-built Image 43 | 44 | You can use the pre-built image straight away - however, this is contains pre-compiled FFmpeg as a result it does not have the following options enabled to comply with FFmpeg licensing; 45 | 46 | - `--enable-nonfree` 47 | - `--enable-libndi_newtek` 48 | - `--enable-decklink` 49 | - `--enable-libsrt` 50 | - `--disable-libaom` 51 | - `--disable-libsvtav1` 52 | - `--enable-libklvanc` 53 | - `--enable-libvmaf` 54 | - `--enable-libfdk-aac` 55 | 56 | If this isn't a problem then you can get the image here; 57 | 58 | `docker pull ghcr.io/ryanmccartney/ffmpeg-docker:latest` 59 | 60 | ## Future Work 61 | 62 | - SRT/RIST statistics - unavalible in FFmpeg, so dificult to report SRT information like, maxRTT, recovered packets, lost packets, jitter, etc. 63 | - SRT Bonding - SRT v1.5 supports bonding through socket groups, again this is not implemented in FFmpeg 64 | 65 | ## About 66 | 67 | This software uses libraries from the FFmpeg project under the LGPLv2.1. 68 | 69 | This software uses code of [FFmpeg](http://ffmpeg.org) licensed under the [LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) and its source can be downloaded [here](https://github.com/ryanmccartney/ffmpeg-docker) 70 | 71 | ### Media Passthrough 72 | 73 | Mounting media from your host machine directly into the running container allows FFmpeg to scan and analyse it in the background. It can then be directly used when running FFmpeg commands. 74 | 75 | Mount media in `/home/node/app/data/media` directory for use. 76 | -------------------------------------------------------------------------------- /docs/pages/installation/decklink.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Decklink 4 | parent: Installation 5 | nav_order: 2 6 | --- 7 | 8 | # Decklink Support 9 | 10 | # Host Machine 11 | 12 | To use the decklink device you'll need to have the correct drivers installed on your host machine. 13 | 14 | You can use the script in this repository to do this; 15 | 16 | - `chmod +x setup.sh` 17 | - `./setup.sh` 18 | 19 | If your Decklink card required an update during this process you'll need to reboot the host machine. 20 | 21 | # Building 22 | 23 | Before building you'll need to set the build argument variable `DECKLINK_SUPPORT` to `true`. 24 | 25 | Additionally, you'll need to get a download link for the Blackmagic Design SDK from the [Blackmagic Website](https://www.blackmagicdesign.com/support/download/2438c76b9f734f69b4a914505e50a5ab/Linux). 26 | 27 | It'll come in the form `https://swr.cloud.blackmagicdesign.com/DeckLink/v12.X.X/Blackmagic_DeckLink_SDK_12.X.X.zip?verify=VALUE` 28 | 29 | Then set the build argument variable `DEKSTOP_VIDEO_SDK_URL` to this url. 30 | 31 | When running the container you'll then need to pass through the device. To do this; 32 | 33 | 1. Install Decklink Driver on your host machine. 34 | 2. Check device is connected and update any firmware 35 | 3. Determine the Blackmagic device mapping on the host. Expect something like - `dev/blackmagic/dv0`. 36 | -------------------------------------------------------------------------------- /docs/pages/installation/environment.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Environment Variables 4 | parent: Installation 5 | nav_order: 2 6 | --- 7 | 8 | # Environment Variables 9 | 10 | Docker image environment variables are given below 11 | 12 | | Variable | Default | Required | Type | Description | 13 | | ------------- | -------------- | -------- | ------- | ---------------------------------------------------------------------------------- | 14 | | PORT | 80 | No | Integer | Port that the backend API runs on | 15 | | HOST | localhost | No | String | | 16 | | RATE_LIMIT | 1000 | No | Integer | The number of requests allowed per client to the API in a 5 minute windows | 17 | | NODE_ENV | production | No | String | Options are "production" or "development" | 18 | | WEB_GUI | true | No | Boolean | Determines whether the API runs a simple frontend or not | 19 | | AUTH_ENABLE | false | No | Boolean | Use JWT auth for API | 20 | | AUTH_USER | admin | No | String | Auth username for login | 21 | | AUTH_PASSWORD | ffmp3gap1 | No | String | Auth password for login | 22 | | AUTH_KEY | averysecretkey | No | String | Private key for generating JWT tokens. Change this if you're using authentication | 23 | | MEDIA_PATH | ./data/media | No | String | Directory to keep media in. Useful to change if media is stored in a network share | 24 | | QUEUE_SIZE | 5 | No | Integer | Number of FFMpeg processes that can run simultaneously | 25 | | LOG_FOLDER | logs | No | String | Folder that logs are stored in | 26 | | LOG_NAME | ffmpeg | No | String | Log file names | 27 | | LOG_LEVEL | info | No | String | Logging level "debug", "error", "info", "http" or "warning" | 28 | -------------------------------------------------------------------------------- /docs/pages/installation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Installation 4 | nav_order: 2 5 | has_children: true 6 | --- 7 | 8 | ### With Decklink Support 9 | 10 | Before building you'll need to set the build argument variable `DECKLINK_SUPPORT` to `true`. 11 | 12 | Additionally, you'll need to get a download link for the Blackmagic Design SDK from the [Blackmagic Website](https://www.blackmagicdesign.com/support/download/2438c76b9f734f69b4a914505e50a5ab/Linux). 13 | 14 | It'll come in the form `https://sw.blackmagicdesign.com/DeckLink/v12.x.x/Blackmagic_DeckLink_SDK_12.x.x.zip?Key-Pair-Id=VALUE==&Expires=VALUE` 15 | 16 | Then set the build argument variable `DEKSTOP_VIDEO_SDK_URL` to this url. 17 | 18 | When running the container you'll then need to pass through the device. To do this; 19 | 20 | 1. Install Decklink Driver on your host machine. 21 | 2. Check device is connected and update any firmware 22 | 3. Determine the Blackmagic device mapping on the host. Expect something like - `dev/blackmagic/dv0`. 23 | 24 | ### With NDI Support 25 | 26 | Using a [patch](https://framagit.org/tytan652/ffmpeg-ndi-patch) to re-add Newtec NDI to FFmpeg on building. 27 | 28 | Before building you'll need to set the build argument variable `NDI_SUPPORT` to `true`. 29 | -------------------------------------------------------------------------------- /docs/pages/installation/ndi.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Newtec NDI 4 | parent: Installation 5 | nav_order: 2 6 | --- 7 | 8 | # NDI Support 9 | 10 | NDI is not supported by FFmpeg as of 2018 due to [licensing issues](https://trac.ffmpeg.org/ticket/7589). 11 | 12 | Howevever, a [patch](https://framagit.org/tytan652/ffmpeg-ndi-patch) exists to readd the relevant code to the FFmpeg source - created by [tytan652](https://tytan652.frama.io/). 13 | 14 | ## Building 15 | 16 | To enable NDI inside the container you'll need to set the build argument variable `NDI_SUPPORT` to `true` before building. 17 | 18 | This will download the NDI SDK, unpack it and set the enable flag for NDI in the FFmpeg build configuration. 19 | 20 | ## Sample Commands 21 | 22 | `ffmpeg -i test.mp4 -vf format=rgb32 -c:v libndi_newtek -pix_fmt uyvy422 -f libndi_newtek "NDI Test Stream"` 23 | -------------------------------------------------------------------------------- /docs/pages/installation/prebuilt.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Pre Built Image 4 | parent: Installation 5 | nav_order: 1 6 | --- 7 | 8 | At this point you've either built the image from scratch, pulled it from and internal repository or the public options have the suitable options for your requirements. 9 | 10 | ## Pulling the Image 11 | 12 | You can pull the public image with the following command 13 | 14 | ``` 15 | docker pull ghcr.io/ryanmccartney/ffmpeg-docker:latest 16 | ``` 17 | 18 | ## Running the Image 19 | 20 | For production you should make sure that the source code is not mounted into the container. If the NODE_ENV option is set it should be set to `production`. This is the default if it is not set. 21 | 22 | ### Docker Standalone 23 | 24 | You can run the container with the prebuilt image just pulled using the following commands; 25 | 26 | ``` 27 | docker run -d\ 28 | --network host\ 29 | --volume ffmpeg-docker-data:/home/node/app/data\ 30 | --env PORT=80\ 31 | --env WEB_GUI=true\ 32 | --env NODE_ENV=production\ 33 | ghcr.io/ryanmccartney/ffmpeg-docker:latest 34 | ``` 35 | 36 | ### Docker Compose 37 | 38 | A sample `docker-compose.yml` file for production is shown below; 39 | 40 | ``` 41 | # NAME: docker-compose.yml 42 | # AUTH: Ryan McCartney 43 | # DATE: 30/10/2023 44 | # DESC: Run ffmpeg-docker prebuilt version 45 | 46 | version: "3.8" 47 | 48 | services: 49 | ffmpeg: 50 | image: ghcr.io/ryanmccartney/ffmpeg-docker:latest 51 | restart: always 52 | volumes: 53 | - ./data:/home/node/app/data 54 | environment: 55 | PORT: 80 56 | WEB_GUI: "true" 57 | NODE_ENV: "production" 58 | network_mode: "host" 59 | ``` 60 | 61 | Paste this into a file called `docker-compose.yml` 62 | 63 | Finally run it with `docker compose up -d`. 64 | -------------------------------------------------------------------------------- /docs/pages/ndi/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: NDI 4 | nav_order: 3 5 | has_children: false 6 | --- 7 | 8 | # NDI 9 | 10 | Readding support for NDI is very much a work in progress and remains untested. 11 | 12 | Using a [patch](https://framagit.org/tytan652/ffmpeg-ndi-patch) to re-add Newtec NDI to FFmpeg on building. 13 | 14 | Before building you'll need to set the build argument variable `NDI_SUPPORT` to `true`. 15 | -------------------------------------------------------------------------------- /docs/pages/usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Usage 4 | nav_order: 4 5 | has_children: true 6 | --- 7 | 8 | # Usage 9 | 10 | The image provides a RESTful API exposing a number of endpoints allowing you to perform common functions on media. 11 | 12 | ## Endpoints 13 | 14 | - `api/vmaf` - For running VMAF tests on files and obtaining the results 15 | - `api/system` - For obtaining system stats and status of running jobs 16 | - `api/INPUT/OUTPUT` - For processing media, the api takes the following format 17 | 18 | `INPUT` options include; 19 | 20 | - `file` 21 | - `decklink` 22 | - `hls` 23 | - `udp` 24 | - `rtp` 25 | - `srt` 26 | - `bars` 27 | 28 | `OUTPUT` options include; 29 | 30 | - `file` 31 | - `decklink` 32 | - `hls` 33 | - `udp` 34 | - `rtp` 35 | - `srt` 36 | - `bars` 37 | - `rtmp` 38 | 39 | All commands that run FFmpeg processes in the container are POST requests. They have a common body format for passing arguments with specific variables that are aplicable to indicudual codecs and input/output types 40 | Common body options for all these media proccessing endpoints shown as follows; 41 | 42 | ```json 43 | { 44 | "input": { 45 | "inputVariable1": "specifically applies to chosen input type", 46 | "inputVariable2": "specifically applies to chosen input type" 47 | }, 48 | "output": { 49 | "outputVariable1": "specifically applies to chosen output type", 50 | "outputVariable2": "specifically applies to chosen output type" 51 | }, 52 | "overlay": { 53 | "fontSize": 90, 54 | "line1": "Line 1 Text", 55 | "line2": "Line 2 Text", 56 | "timecode": true, 57 | "font": "swansea-bold.ttf", 58 | "offset": 1, 59 | "topRight": { 60 | "line1": "%{pts\\:hms}", 61 | "line2": "Frame %{n}" 62 | } 63 | }, 64 | "thumbnail": { 65 | "frequency": 25 66 | } 67 | } 68 | ``` 69 | 70 | ## Media 71 | 72 | Media can be added into the container at `home/node/app/data/media`. This can then be called relative to this directory. 73 | 74 | For example `home/node/app/data/media/test.mp4` can be called using the `file` parameter in a request body as `test.mp4`. 75 | -------------------------------------------------------------------------------- /docs/pages/vmaf/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: VMAF 4 | nav_order: 4 5 | has_children: false 6 | --- 7 | 8 | # VMAF Support 9 | 10 | [VMAF](https://github.com/Netflix/vmaf) - Video Multi-Method Assessment Fusion is a [Netflix](https://github.com/Netflix) Open Source project for evaluating the quality of video files. 11 | 12 | FFmpeg-Docker provides build instructions for VMAF by default, and exposes an API endpoint for processing files with VMAF and generating results. 13 | 14 | Media files can be processes relative to the `media` directory and result exported to a `MY-VMAF-TEST.json` file. 15 | 16 | ## Endpoints 17 | 18 | - `api/vmaf` 19 | 20 | ## Running a VMAF test 21 | 22 | GET - `api/vmaf/file` 23 | 24 | Request Body 25 | 26 | ``` 27 | { 28 | "input": { 29 | "filename": "test-file.mov" 30 | }, 31 | "reference": { 32 | "filename": "reference-file.mov" 33 | }, 34 | "output": "test-output.json", 35 | "model": "vmaf_v0.6.1.json" 36 | } 37 | ``` 38 | 39 | Alternatively, running from the broswers 40 | 41 | `http://SERVER_IP:SERVER_PORT/api/vmaf/test?filename=test-file.mov&reference=reference-file.mov` 42 | 43 | Running the above command again after starting the test will display information about the task progress. 44 | 45 | If you need to stop the current command running you can use the following command; 46 | 47 | `http://SERVER_IP:SERVER_PORT/api/vmaf/test?kill=true` 48 | 49 | ## Underlying Command 50 | 51 | Sending the above API command results in the following underlying command being run on the server is; 52 | 53 | `ffmpeg -i test-file.mov -i reference-file.mov -y -lavfi libvmaf=model_path=/ffmpeg_sources/vmaf/model/vmaf_v0.6.1.json:log_fmt=json:psnr=1:ssim=1:ms_ssim=1:log_path=test-output.json -f null -` 54 | 55 | ## Generating Graphs from the outputs 56 | 57 | In the example above the parameter `output` was set to `test-output.json`. This means that all of the results are saved in text to this file. 58 | 59 | This can be directly downloaded as follows 60 | 61 | - As a JSON object - `http://SERVER_IP:SERVER_PORT/api/vmaf/results/json?filename=test-output.json` 62 | - As a CSV object - `http://SERVER_IP:SERVER_PORT/api/vmaf/results/csv?filename=test-output.json` 63 | - As a JSON file - `http://SERVER_IP:SERVER_PORT/api/vmaf/results/download/json?filename=test-output.json` 64 | - As a CSV file - `http://SERVER_IP:SERVER_PORT/api/vmaf/results/download/csv?filename=test-output.json` 65 | 66 | A graph of the results can be generated here - `http://SERVER_IP:SERVER_PORT/html/chart.html?filename=test-output.json` using Chart.js. 67 | 68 | Rememebr to change the filename query in the URL to the one set as the `output` parameter when running the test. 69 | 70 | ## Docker Build 71 | 72 | ``` 73 | # Add source and compile for Netflix VMAF support 74 | RUN git -C libvmaf pull 2> /dev/null || git clone --depth 1 https://github.com/Netflix/vmaf.git && \ 75 | cd ./vmaf/libvmaf && \ 76 | apt -y install ninja-build meson && \ 77 | meson build --buildtype release && \ 78 | ninja -vC build && \ 79 | ninja -vC build install 80 | ``` 81 | 82 | Adding VMAF support to FFmpeg Build configuration 83 | 84 | ``` 85 | RUN ./configure \ 86 | ... 87 | --enable-libvmaf 88 | ``` 89 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const register = require("module-alias/register"); 25 | const app = require("@bin/app"); 26 | const logger = require("@utils/logger")(module); 27 | const http = require("http"); 28 | 29 | const port = process.env.PORT || "80"; 30 | app.set("port", port); 31 | 32 | const server = http.createServer(app); 33 | 34 | const serve = async () => { 35 | try { 36 | server.on("error", onError); 37 | server.on("listening", onListening); 38 | server.listen(port); 39 | } catch (error) { 40 | throw error; 41 | } 42 | }; 43 | 44 | const onError = (error) => { 45 | if (error.syscall !== "listen") { 46 | throw error; 47 | } 48 | 49 | const bind = typeof port === "string" ? "Pipe " + port : "Port " + port; 50 | 51 | // handle specific listen errors with friendly messages 52 | switch (error.code) { 53 | case "EACCES": 54 | logger.error(bind + " requires elevated privileges"); 55 | process.exit(1); 56 | break; 57 | case "EADDRINUSE": 58 | logger.error(bind + " is already in use"); 59 | process.exit(1); 60 | break; 61 | default: 62 | throw error; 63 | } 64 | }; 65 | 66 | const onListening = () => { 67 | const addr = server.address(); 68 | const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; 69 | logger.info(`ffmpeg listening on ${bind}`); 70 | }; 71 | 72 | serve(); 73 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/logs/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ffmpeg-docker", 3 | "version": "1.0.0", 4 | "description": "FFmpeg compiled in a docker container with an API for control", 5 | "main": "index.js", 6 | "scripts": { 7 | "development": "nodemon index", 8 | "production": "node index", 9 | "docs": "node ./utils/markdown && swagger-markdown --force-version 2 -i ./docs/assets/api-spec.yml -o ./docs/pages/usage/endpoints.md" 10 | }, 11 | "author": "Ryan McCartney", 12 | "license": "GPLv3", 13 | "dependencies": { 14 | "bootstrap": "^5.3.2", 15 | "bootstrap-icons": "^1.11.1", 16 | "chart.js": "^4.4.0", 17 | "check-disk-space": "^3.4.0", 18 | "cookie-parser": "^1.4.6", 19 | "cors": "^2.8.5", 20 | "date-fns": "^2.30.0", 21 | "express": "^4.18.1", 22 | "express-async-handler": "^1.2.0", 23 | "express-rate-limit": "^7.1.4", 24 | "express-validator": "^7.0.1", 25 | "fluent-ffmpeg": "^2.1.2", 26 | "gaugeJS": "^1.3.7", 27 | "helmet": "^5.0.1", 28 | "jest": "^29.7.0", 29 | "jsonwebtoken": "^9.0.2", 30 | "md5": "^2.3.0", 31 | "module-alias": "^2.2.2", 32 | "morgan": "^1.10.0", 33 | "multer": "^1.4.5-lts.1", 34 | "mustache": "^4.2.0", 35 | "mustache-express": "^1.3.2", 36 | "nanoid": "^4.0.0", 37 | "node-cache": "^5.1.2", 38 | "node-gyp": "^9.4.0", 39 | "ntp-time": "^2.0.2", 40 | "os": "^0.1.2", 41 | "passport": "^0.6.0", 42 | "passport-jwt": "^4.0.1", 43 | "pidusage": "^3.0.2", 44 | "qrcode": "^1.5.3", 45 | "serve-favicon": "^2.5.0", 46 | "sharp": "^0.32.6", 47 | "swagger-jsdoc": "^6.0.2", 48 | "swagger-ui-express": "^4.3.0", 49 | "uuid": "^8.3.2", 50 | "validator": "^13.11.0", 51 | "video.js": "^8.6.1", 52 | "winston": "^3.3.3", 53 | "winston-daily-rotate-file": "^4.5.5", 54 | "winston-mongodb": "^5.0.7", 55 | "yaml": "^2.3.1" 56 | }, 57 | "optionalDependencies": { 58 | "macadam": "^2.0.18" 59 | }, 60 | "devDependencies": { 61 | "nodemon": "^2.0.22", 62 | "swagger-markdown": "^2.3.0" 63 | }, 64 | "nodemonConfig": { 65 | "ignore": [ 66 | "./public/*", 67 | "./data/*", 68 | "./docs/*" 69 | ] 70 | }, 71 | "_moduleAliases": { 72 | "@root": ".", 73 | "@bin": "./bin/", 74 | "@routes": "./routes/", 75 | "@utils": "./utils/", 76 | "@services": "./services/", 77 | "@validators": "./validators/" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /public/fonts/short-baby.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/public/fonts/short-baby.ttf -------------------------------------------------------------------------------- /public/fonts/swansea-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/public/fonts/swansea-bold.ttf -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmccartney/ffmpeg-docker/bce5f16f219694c2fe29d9fce754f018e7d12699/public/images/favicon.ico -------------------------------------------------------------------------------- /public/js/chart.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const queryString = window.location.search; 23 | const urlParams = new URLSearchParams(queryString); 24 | 25 | getData(); 26 | 27 | async function getData() { 28 | const file = urlParams.get("file"); 29 | const frames = []; 30 | const psnr = []; 31 | const vmaf = []; 32 | 33 | const response = await fetch(`/api/vmaf/results/json?file=${file}`); 34 | const data = await response.json(); 35 | 36 | const framesData = data.data.frames; 37 | for (let frame of framesData) { 38 | frames.push(frame.frameNum); 39 | psnr.push(frame.metrics.psnr); 40 | vmaf.push(frame.metrics.vmaf); 41 | } 42 | 43 | new Chart("myChart", { 44 | type: "line", 45 | data: { 46 | labels: frames, 47 | datasets: [ 48 | { 49 | label: "PSNR", 50 | fill: false, 51 | lineTension: 0, 52 | backgroundColor: "rgba(0,0,255,0.6)", 53 | borderColor: "rgba(0,0,255,0.6)", 54 | pointRadius: 0, 55 | data: psnr, 56 | }, 57 | { 58 | label: "VMAF", 59 | fill: false, 60 | lineTension: 0, 61 | backgroundColor: "rgba(255,0,0,0.6)", 62 | borderColor: "rgba(255,0,0,0.6)", 63 | pointRadius: 0, 64 | data: vmaf, 65 | }, 66 | ], 67 | }, 68 | options: { 69 | plugins: { 70 | legend: { 71 | position: "bottom", 72 | display: true, 73 | }, 74 | title: { 75 | display: true, 76 | text: `VMAF Results for ${file}`, 77 | }, 78 | }, 79 | responsive: true, 80 | scales: { 81 | x: { 82 | display: true, 83 | title: { 84 | display: true, 85 | text: "Frames", 86 | padding: { top: 20, left: 0, right: 0, bottom: 0 }, 87 | }, 88 | ticks: { 89 | stepSize: 10, 90 | }, 91 | }, 92 | y: { 93 | display: true, 94 | title: { 95 | display: true, 96 | text: "Percentage (0-100%)", 97 | }, 98 | max: 100, 99 | min: 0, 100 | ticks: { 101 | stepSize: 2, 102 | }, 103 | padding: { top: 20, left: 0, right: 0, bottom: 0 }, 104 | }, 105 | }, 106 | }, 107 | }); 108 | } 109 | -------------------------------------------------------------------------------- /public/js/clock.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | let offset = 0; 23 | getTime(); 24 | 25 | function updateClock() { 26 | var now = new Date(new Date() - offset); 27 | var hours = now.getHours(); 28 | var minutes = now.getMinutes(); 29 | var seconds = now.getSeconds(); 30 | var milliseconds = now.getMilliseconds(); 31 | 32 | hours = hours.toString().padStart(2, "0"); 33 | minutes = minutes.toString().padStart(2, "0"); 34 | seconds = seconds.toString().padStart(2, "0"); 35 | milliseconds = Math.floor(milliseconds / 100).toString(); 36 | 37 | var time = hours + ":" + minutes + ":" + seconds; 38 | var millisecondsText = milliseconds; 39 | document.getElementById("clock").textContent = time; 40 | document.getElementById("milliseconds").textContent = "." + millisecondsText; 41 | } 42 | 43 | async function getTime() { 44 | const startTime = performance.now(); 45 | const response = await fetch(`/api/system/time`); 46 | const data = await response.json(); 47 | const endTime = performance.now(); 48 | const duration = endTime - startTime; 49 | 50 | if (data.data.datatime) { 51 | const serverTime = new Date(new Date(data.data.datatime) - duration); 52 | offset = new Date() - serverTime; 53 | 54 | console.log(`Request toook ${duration}ms`); 55 | console.log(`Server time is ${serverTime}`); 56 | console.log(`Offset time is ${offset}ms`); 57 | document.getElementById("subtitle").textContent = `Synced to ${location.hostname}. Offset is ${offset}ms`; 58 | } else { 59 | document.getElementById("subtitle").textContent = `Could not sync with server. Offset is ${offset}ms`; 60 | } 61 | 62 | setInterval(updateClock, 1); // Update every millisecond 63 | } 64 | -------------------------------------------------------------------------------- /public/js/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const gaugeOptions = { 23 | angle: 0, // The span of the gauge arc 24 | lineWidth: 0.44, // The line thickness 25 | radiusScale: 1, // Relative radius 26 | pointer: { 27 | length: 0.6, // // Relative to gauge radius 28 | strokeWidth: 0.035, // The thickness 29 | color: "#000000", // Fill color 30 | }, 31 | limitMax: false, // If false, max value increases automatically if value > maxValue 32 | limitMin: false, // If true, the min value of the gauge will be fixed 33 | percentColors: [ 34 | [0.25, "#0BD70B"], 35 | [0.5, "#FFB300"], 36 | [1.0, "#FF3300"], 37 | ], 38 | staticLabels: { 39 | font: "10px sans-serif", // Specifies font 40 | labels: [0, 100], // Print labels at these values 41 | color: "#000000", // Optional: Label text color 42 | fractionDigits: 0, 43 | }, 44 | strokeColor: "#E0E0E0", 45 | generateGradient: false, 46 | highDpiSupport: true, // High resolution support 47 | }; 48 | 49 | const gaugeCpuElement = document.getElementById("gaugeCpu"); 50 | const gaugeMemoryElement = document.getElementById("gaugeMemory"); 51 | const gaugeDiskElement = document.getElementById("gaugeDisk"); 52 | 53 | const textCpuElement = document.getElementById("textCpu"); 54 | const textMemoryElement = document.getElementById("textMemory"); 55 | const textDiskElement = document.getElementById("textDisk"); 56 | 57 | const gaugeCpu = new Gauge(gaugeCpuElement).setOptions(gaugeOptions); 58 | const gaugeMemory = new Gauge(gaugeMemoryElement).setOptions(gaugeOptions); 59 | const gaugeDisk = new Gauge(gaugeDiskElement).setOptions(gaugeOptions); 60 | 61 | gaugeCpu.maxValue = 100; 62 | gaugeCpu.setMinValue(0); 63 | gaugeCpu.set(0); 64 | 65 | gaugeMemory.maxValue = 100; 66 | gaugeMemory.setMinValue(0); 67 | gaugeMemory.set(0); 68 | 69 | gaugeDisk.maxValue = 100; 70 | gaugeDisk.setMinValue(0); 71 | gaugeDisk.set(0); 72 | 73 | const getData = async () => { 74 | const response = await fetch(`/api/system/stats`); 75 | const data = await response.json(); 76 | 77 | gaugeCpu.set(data?.load * 100); 78 | gaugeMemory.set(data?.memory?.usage * 100); 79 | gaugeDisk.set(data?.disk?.usage * 100); 80 | 81 | textCpuElement.innerHTML = `${Math.round(data?.load * 100)}%`; 82 | textMemoryElement.innerHTML = `${Math.round(data?.memory?.usage * 100)}% (${Math.round( 83 | (data?.memory?.total - data?.memory?.free) / 1000000000 84 | )}GB / ${Math.round(data?.memory?.total / 1000000000)}GB)`; 85 | 86 | textDiskElement.innerHTML = `${Math.round(data?.disk?.usage * 100)}% (${Math.round( 87 | (data?.disk?.size - data?.disk?.free) / 1000000000 88 | )}GB / ${Math.round(data?.disk?.size / 1000000000)}GB)`; 89 | }; 90 | 91 | getData(); 92 | const interval = setInterval(getData, 1000); 93 | -------------------------------------------------------------------------------- /public/js/jobs.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const table = document.getElementById("jobTable"); 23 | let interval; 24 | 25 | getJobs(); 26 | 27 | async function killJob(jobId) { 28 | const response = await fetch(`/api/system/job/kill/${jobId}`, { 29 | method: "POST", 30 | }); 31 | console.log(`Killed Job ID ${jobId}`); 32 | } 33 | 34 | async function killAll() { 35 | const response = await fetch(`/api/system/job/kill/all`, { 36 | method: "POST", 37 | }); 38 | console.log(`Killed all Jobs`); 39 | } 40 | 41 | async function getJobs() { 42 | for (let i = table.rows.length; i >= 1; i--) { 43 | try { 44 | await table.deleteRow(i); 45 | } catch (error) { 46 | console.log(error); 47 | } 48 | } 49 | 50 | const response = await fetch(`/api/system/job/all`); 51 | const data = await response.json(); 52 | 53 | for (let [jobId, job] of Object.entries(data?.jobs)) { 54 | const row = await table.insertRow(table.rows.length); 55 | 56 | const cell1 = await row.insertCell(0); 57 | const cell2 = await row.insertCell(1); 58 | const cell3 = await row.insertCell(2); 59 | const cell4 = await row.insertCell(3); 60 | const cell5 = await row.insertCell(4); 61 | const cell6 = await row.insertCell(5); 62 | const cell7 = await row.insertCell(6); 63 | const cell8 = await row.insertCell(7); 64 | const cell9 = await row.insertCell(8); 65 | 66 | // Add some text to the new cells: 67 | cell1.innerHTML = job?.jobNumber || "-"; 68 | cell2.innerHTML = job?.jobName || "-"; 69 | cell3.innerHTML = job?.jobId || "-"; 70 | 71 | for (let tag of job?.type) { 72 | if (tag === "file") { 73 | cell4.innerHTML += `${tag}`; 74 | } else if (tag === "bars") { 75 | cell4.innerHTML += `${tag}`; 76 | } else if (tag === "decklink") { 77 | cell4.innerHTML += `${tag}`; 78 | } else { 79 | cell4.innerHTML += `${tag}`; 80 | } 81 | } 82 | 83 | cell5.innerHTML = job?.started || "-"; 84 | cell6.innerHTML = `${Math.round(job?.load?.cpu * 100)}%`; 85 | cell7.innerHTML = job?.progress 86 | ? `
87 |
${job?.progress}% 88 |
` 89 | : "-"; 90 | cell8.innerHTML = `No Image`; 91 | cell9.innerHTML = ``; 92 | } 93 | } 94 | 95 | interval = setInterval(getJobs, 2000); 96 | -------------------------------------------------------------------------------- /public/js/video.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const queryString = window.location.search; 23 | const urlParams = new URLSearchParams(queryString); 24 | const file = urlParams.get("file"); 25 | const filePath = `/api/hls/${file}.m3u8`; 26 | 27 | const options = { 28 | sources: [ 29 | { 30 | src: filePath, 31 | type: "application/x-mpegURL", 32 | }, 33 | ], 34 | fluid: true, 35 | }; 36 | 37 | const player = videojs("video-player", options, function onPlayerReady() { 38 | videojs.log("Video player is ready."); 39 | this.play(); 40 | }); 41 | -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const { checkSchema, validationResult } = require("express-validator"); 26 | const hashResponse = require("@utils/hash-response"); 27 | const jwt = require("jsonwebtoken"); 28 | const md5 = require("md5"); 29 | 30 | const authValidator = require("@validators/auth"); 31 | 32 | /** 33 | * @swagger 34 | * /auth/login: 35 | * post: 36 | * description: Authorize user. 37 | * tags: [auth] 38 | * produces: 39 | * - application/json 40 | * parameters: 41 | * - in: formData 42 | * name: username 43 | * type: string 44 | * description: Username for API by default "admin" 45 | * required: false 46 | * - in: formData 47 | * name: password 48 | * type: string 49 | * description: Password for API by default "ffmp3gap1" 50 | * required: false 51 | * responses: 52 | * '200': 53 | * description: Success 54 | */ 55 | router.post("/login", checkSchema(authValidator), async (req, res, next) => { 56 | let response = {}; 57 | const errors = await validationResult(req); 58 | 59 | if (errors.isEmpty()) { 60 | const user = process.env.AUTH_USER || "admin"; 61 | const passwordHash = md5(process.env.AUTH_PASSWORD || "ffmp3gap1"); 62 | 63 | if (req.body.username === user && md5(req.body.password) === passwordHash) { 64 | const token = jwt.sign({ id: user }, process.env.AUTH_KEY || "averysecretkey"); 65 | response.token = token; 66 | } else { 67 | response.errors = ["Username or password is incorrect"]; 68 | } 69 | } else { 70 | response.errors = errors.array(); 71 | } 72 | 73 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 74 | }); 75 | 76 | module.exports = router; 77 | -------------------------------------------------------------------------------- /routes/hls.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const express = require("express"); 26 | const path = require("path"); 27 | 28 | /** 29 | * @swagger 30 | * /hls: 31 | * get: 32 | * description: Servers the HLS manifest files. 33 | * tags: [hls] 34 | * produces: 35 | * - application/json 36 | * responses: 37 | * '200': 38 | * description: Success 39 | */ 40 | router.use("/", express.static(path.join(__dirname, "..", "data", "hls"))); 41 | 42 | module.exports = router; 43 | -------------------------------------------------------------------------------- /routes/page.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const path = require("path"); 26 | 27 | /** 28 | * @swagger 29 | * /: 30 | * get: 31 | * description: Index page if web GUI option is enabled 32 | * tags: [page] 33 | * produces: 34 | * - application/json 35 | * responses: 36 | * '200': 37 | * description: Success 38 | */ 39 | router.get("/", async (req, res, next) => { 40 | res.render("index", { title: "Home" }); 41 | }); 42 | 43 | /** 44 | * @swagger 45 | * /clock: 46 | * get: 47 | * description: An HTML page with a live clock showing server time - suitable for latency measurements 48 | * tags: [page] 49 | * produces: 50 | * - application/json 51 | * responses: 52 | * '200': 53 | * description: Success 54 | */ 55 | router.get("/clock", async (req, res, next) => { 56 | res.render("clock", { title: "Clock" }); 57 | }); 58 | 59 | /** 60 | * @swagger 61 | * /jobs: 62 | * get: 63 | * description: An HTML page showing a simple job manager 64 | * tags: [page] 65 | * produces: 66 | * - application/json 67 | * responses: 68 | * '200': 69 | * description: Success 70 | */ 71 | router.get("/jobs", async (req, res, next) => { 72 | res.render("jobs", { title: "Jobs" }); 73 | }); 74 | 75 | /** 76 | * @swagger 77 | * /chart: 78 | * get: 79 | * description: An HTML page showing VMAF test results as a chart 80 | * tags: [page] 81 | * produces: 82 | * - application/json 83 | * responses: 84 | * '200': 85 | * description: Success 86 | */ 87 | router.get("/chart", async (req, res, next) => { 88 | res.render("chart", { title: "Chart" }); 89 | }); 90 | 91 | /** 92 | * @swagger 93 | * /video: 94 | * get: 95 | * description: An HTML page with a video player for hls streams 96 | * tags: [page] 97 | * produces: 98 | * - application/json 99 | * responses: 100 | * '200': 101 | * description: Success 102 | */ 103 | router.get("/video", async (req, res, next) => { 104 | res.render("video", { title: "Video" }); 105 | }); 106 | 107 | module.exports = router; 108 | -------------------------------------------------------------------------------- /routes/playlist.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const { checkSchema, validationResult } = require("express-validator"); 26 | const hashResponse = require("@utils/hash-response"); 27 | const ffconcat = require("@utils/ffconcat"); 28 | 29 | /** 30 | * @swagger 31 | * /playlist/:playlist: 32 | * get: 33 | * description: Get all the items in a playlist 34 | * tags: [files] 35 | * produces: 36 | * - application/json 37 | * responses: 38 | * '200': 39 | * description: Success 40 | */ 41 | router.get("/:playlist", async (req, res, next) => { 42 | const response = await ffconcat.getItems(req.params.playlist); 43 | hashResponse(res, req, { ...response, ...{ status: response.error ? "error" : "success" } }); 44 | }); 45 | 46 | /** 47 | * @swagger 48 | * /playlist/:playlist: 49 | * post: 50 | * description: Set all the items in a playlist 51 | * tags: [files] 52 | * produces: 53 | * - application/json 54 | * responses: 55 | * '200': 56 | * description: Success 57 | */ 58 | router.post("/:playlist", async (req, res, next) => { 59 | const response = await ffconcat.set(req.params.playlist, req.body.items); 60 | hashResponse(res, req, { ...response, ...{ status: response.error ? "error" : "success" } }); 61 | }); 62 | 63 | /** 64 | * @swagger 65 | * /playlist/:playlist/add: 66 | * post: 67 | * description: Add a single file to the playlist. 68 | * tags: [files] 69 | * produces: 70 | * - application/json 71 | * responses: 72 | * '200': 73 | * description: Success 74 | */ 75 | router.post("/:playlist/add", async (req, res, next) => { 76 | const response = await ffconcat.add(req.params.playlist, req.body.item); 77 | hashResponse(res, req, { ...response, ...{ status: response.error ? "error" : "success" } }); 78 | }); 79 | 80 | /** 81 | * @swagger 82 | * /playlist/:playlist/remove: 83 | * post: 84 | * description: Add a single file to the playlist. 85 | * tags: [files] 86 | * produces: 87 | * - application/json 88 | * responses: 89 | * '200': 90 | * description: Success 91 | */ 92 | router.post("/:playlist/remove", async (req, res, next) => { 93 | const response = await ffconcat.remove(req.params.playlist, req.body.item); 94 | hashResponse(res, req, { ...response, ...{ status: response.error ? "error" : "success" } }); 95 | }); 96 | 97 | module.exports = router; 98 | -------------------------------------------------------------------------------- /routes/rtp.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const { checkSchema, validationResult } = require("express-validator"); 26 | const hashResponse = require("@utils/hash-response"); 27 | 28 | const rtpFile = require("@services/rtp-file"); 29 | const rtpDecklink = require("@services/rtp-decklink"); 30 | 31 | const overlayValidator = require("@validators/overlay"); 32 | const thumbnailValidator = require("@validators/thumbnail"); 33 | const decklinkValidator = require("@validators/decklink"); 34 | const fileValidator = require("@validators/file"); 35 | const rtpValidator = require("@validators/rtp"); 36 | 37 | /** 38 | * @swagger 39 | * /rtp/file: 40 | * post: 41 | * description: Takes an RTP input and turns it into a file. 42 | * tags: [rtp] 43 | * produces: 44 | * - application/json 45 | * responses: 46 | * '200': 47 | * description: Success 48 | */ 49 | router.post( 50 | "/file", 51 | checkSchema({ 52 | ...rtpValidator("input"), 53 | ...fileValidator("output"), 54 | ...thumbnailValidator(), 55 | ...overlayValidator(), 56 | }), 57 | async (req, res, next) => { 58 | let response = {}; 59 | const errors = await validationResult(req); 60 | 61 | if (errors.isEmpty()) { 62 | response = await rtpFile(req.body); 63 | } else { 64 | response.errors = errors.array(); 65 | } 66 | 67 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 68 | } 69 | ); 70 | 71 | /** 72 | * @swagger 73 | * /rtp/decklink: 74 | * post: 75 | * description: Takes an RTP input and outputs it to a decklink card. 76 | * tags: [rtp] 77 | * produces: 78 | * - application/json 79 | * responses: 80 | * '200': 81 | * description: Success 82 | */ 83 | router.post( 84 | "/decklink", 85 | checkSchema({ 86 | ...rtpValidator("input"), 87 | ...decklinkValidator("output"), 88 | ...thumbnailValidator(), 89 | ...overlayValidator(), 90 | }), 91 | async (req, res, next) => { 92 | let response = {}; 93 | const errors = await validationResult(req); 94 | 95 | if (errors.isEmpty()) { 96 | response = await rtpDecklink(req.body); 97 | } else { 98 | response.errors = errors.array(); 99 | } 100 | 101 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 102 | } 103 | ); 104 | 105 | module.exports = router; 106 | -------------------------------------------------------------------------------- /routes/srt.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const { checkSchema, validationResult } = require("express-validator"); 26 | const hashResponse = require("@utils/hash-response"); 27 | 28 | const srtFile = require("@services/srt-file"); 29 | const srtDecklink = require("@services/srt-decklink"); 30 | 31 | const overlayValidator = require("@validators/overlay"); 32 | const thumbnailValidator = require("@validators/thumbnail"); 33 | const decklinkValidator = require("@validators/decklink"); 34 | const fileValidator = require("@validators/file"); 35 | const srtValidator = require("@validators/srt"); 36 | 37 | /** 38 | * @swagger 39 | * /srt/file: 40 | * post: 41 | * description: Takes an SRT input and turns it into a file. 42 | * tags: [srt] 43 | * produces: 44 | * - application/json 45 | * responses: 46 | * '200': 47 | * description: Success 48 | */ 49 | router.post( 50 | "/file", 51 | checkSchema({ 52 | ...srtValidator("input"), 53 | ...fileValidator("output"), 54 | ...thumbnailValidator(), 55 | ...overlayValidator(), 56 | }), 57 | async (req, res, next) => { 58 | let response = {}; 59 | const errors = await validationResult(req); 60 | 61 | if (errors.isEmpty()) { 62 | response = await srtFile(req.body); 63 | } else { 64 | response.errors = errors.array(); 65 | } 66 | 67 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 68 | } 69 | ); 70 | 71 | /** 72 | * @swagger 73 | * /srt/decklink: 74 | * post: 75 | * description: Takes an SRT input and outputs it to a decklink card. 76 | * tags: [srt] 77 | * produces: 78 | * - application/json 79 | * responses: 80 | * '200': 81 | * description: Success 82 | */ 83 | router.post( 84 | "/decklink", 85 | checkSchema({ 86 | ...srtValidator("input"), 87 | ...decklinkValidator("output"), 88 | ...thumbnailValidator(), 89 | ...overlayValidator(), 90 | }), 91 | async (req, res, next) => { 92 | let response = {}; 93 | const errors = await validationResult(req); 94 | 95 | if (errors.isEmpty()) { 96 | response = await srtDecklink(req.body); 97 | } else { 98 | response.errors = errors.array(); 99 | } 100 | 101 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 102 | } 103 | ); 104 | 105 | module.exports = router; 106 | -------------------------------------------------------------------------------- /routes/udp.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const router = require("express").Router(); 25 | const { checkSchema, validationResult } = require("express-validator"); 26 | const hashResponse = require("@utils/hash-response"); 27 | 28 | const udpDecklink = require("@services/udp-decklink"); 29 | const udpFile = require("@services/udp-file"); 30 | 31 | const overlayValidator = require("@validators/overlay"); 32 | const thumbnailValidator = require("@validators/thumbnail"); 33 | const decklinkValidator = require("@validators/decklink"); 34 | const fileValidator = require("@validators/file"); 35 | const udpValidator = require("@validators/udp"); 36 | 37 | /** 38 | * @swagger 39 | * /udp/decklink: 40 | * post: 41 | * description: Takes an UDP input and outputs it to a decklink card. 42 | * tags: [udp] 43 | * produces: 44 | * - application/json 45 | * responses: 46 | * '200': 47 | * description: Success 48 | */ 49 | router.post( 50 | "/decklink", 51 | checkSchema({ 52 | ...udpValidator("input"), 53 | ...decklinkValidator("output"), 54 | ...thumbnailValidator(), 55 | ...overlayValidator(), 56 | }), 57 | async (req, res, next) => { 58 | let response = {}; 59 | const errors = await validationResult(req); 60 | 61 | if (errors.isEmpty()) { 62 | response = await udpDecklink(req.body); 63 | } else { 64 | response.errors = errors.array(); 65 | } 66 | 67 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 68 | } 69 | ); 70 | 71 | /** 72 | * @swagger 73 | * /udp/file: 74 | * post: 75 | * description: Takes an UDP input and outputs it to a file. 76 | * tags: [udp] 77 | * produces: 78 | * - application/json 79 | * responses: 80 | * '200': 81 | * description: Success 82 | */ 83 | router.post( 84 | "/file", 85 | checkSchema({ 86 | ...udpValidator("input"), 87 | ...fileValidator("output"), 88 | ...thumbnailValidator(), 89 | ...overlayValidator(), 90 | }), 91 | async (req, res, next) => { 92 | let response = {}; 93 | const errors = await validationResult(req); 94 | 95 | if (errors.isEmpty()) { 96 | response = await udpFile(req.body); 97 | } else { 98 | response.errors = errors.array(); 99 | } 100 | 101 | hashResponse(res, req, { ...response, ...{ status: response.errors ? "error" : "success" } }); 102 | } 103 | ); 104 | 105 | module.exports = router; 106 | -------------------------------------------------------------------------------- /services/bars-decklink.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const ffmpeg = require("fluent-ffmpeg"); 26 | const path = require("path"); 27 | const filterCombine = require("@utils/filter-combine"); 28 | const filterText = require("@utils/filter-text"); 29 | const filterImage = require("@utils/filter-image"); 30 | const jobManager = require("@utils/jobManager"); 31 | 32 | const process = async (options) => { 33 | const response = { options: options }; 34 | 35 | ffmpeg.setFfmpegPath("/root/bin/ffmpeg"); 36 | 37 | try { 38 | const job = jobManager.start(`${options?.output?.cardName}-out`, `Bars to ${options?.output?.cardName}`, [ 39 | "bars", 40 | "decklink", 41 | ]); 42 | 43 | const filters = await filterCombine(await filterText({ ...options, ...job })); 44 | 45 | let command = ffmpeg({ logger: logger }) 46 | .addInput(`${options.input?.type || "smptehdbars"}=rate=25:size=1920x1080`) 47 | .inputOptions(["-re", "-f lavfi"]) 48 | .addInput(`sine=frequency=${options.input?.frequency || 1000}:sample_rate=48000`) 49 | .inputOptions(["-f lavfi"]) 50 | .outputOptions([ 51 | "-pix_fmt uyvy422", 52 | "-s 1920x1080", 53 | "-ac 16", 54 | "-f decklink", 55 | `-af volume=${options?.output?.volume || 0.25}`, 56 | "-flags low_delay", 57 | "-bufsize 0", 58 | "-muxdelay 0", 59 | ]) 60 | .output(options.output?.cardName); 61 | 62 | if (Array.isArray(filters)) { 63 | command.videoFilters(filters); 64 | } 65 | 66 | if (options?.thumbnail !== false) { 67 | command 68 | .output(path.join(__dirname, "..", "data", "thumbnail", `${job?.jobId}.png`)) 69 | .outputOptions([`-r ${options?.thumbnail?.frequency || 1}`, "-update 1"]); 70 | 71 | if (Array.isArray(filters)) { 72 | command.videoFilters(filters); 73 | } 74 | } 75 | 76 | command.on("end", () => { 77 | logger.info("Finished processing"); 78 | jobManager.end(job?.jobId, false); 79 | }); 80 | 81 | command.on("start", (commandString) => { 82 | logger.debug(`Spawned FFmpeg with command: ${commandString}`); 83 | response.job = jobManager.update(job?.jobId, { 84 | command: commandString, 85 | pid: command.ffmpegProc.pid, 86 | options: options, 87 | }); 88 | return response; 89 | }); 90 | 91 | command.on("stderr", (stderrLine) => { 92 | logger.info("ffmpeg: " + stderrLine); 93 | }); 94 | 95 | command.on("error", (error) => { 96 | logger.error(error); 97 | jobManager.end(job?.jobId, false); 98 | }); 99 | 100 | command.run(); 101 | } catch (error) { 102 | logger.error(error.message); 103 | response.errors = [error]; 104 | } 105 | 106 | response.job = await jobManager.get(`${options?.output?.cardName}-out`); 107 | return response; 108 | }; 109 | 110 | module.exports = process; 111 | -------------------------------------------------------------------------------- /services/decklink-config-get.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const logger = require("@utils/logger")(module); 23 | const os = require("os"); 24 | 25 | module.exports = async () => { 26 | if (os.arch() === "x64" && os.platform() === "linux") { 27 | const os = require("os"); 28 | const macadam = require("macadam"); 29 | const deviceInfo = await macadam.getDeviceInfo(); 30 | return { devices: deviceInfo }; 31 | } else { 32 | logger.error(`Invalid Architecture - this command is not supported on ${os.platform()}-${os.arch()}`); 33 | return { 34 | error: "Invalid Architecture - this command is not supported on your system architecture", 35 | arch: os.arch(), 36 | platform: os.platform(), 37 | }; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /services/decklink-config-set.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | const logger = require("@utils/logger")(module); 23 | const os = require("os"); 24 | 25 | module.exports = async (options) => { 26 | if (os.arch() === "x64" && os.platform() === "linux") { 27 | const macadam = require("macadam"); 28 | let status; 29 | try { 30 | status = await macadam.setDeviceConfig({ 31 | deviceIndex: parseInt(options?.cardIndex || 0), 32 | duplexMode: macadam.bmdDuplexModeFull, 33 | }); 34 | } catch (error) { 35 | status = false; 36 | logger.warn(error); 37 | } 38 | return status; 39 | } else { 40 | logger.error(`Invalid Architecture - this command is not supported on ${os.arch()}`); 41 | return { 42 | error: "Invalid Architecture - this command is not supported on your system architecture", 43 | arch: os.arch(), 44 | }; 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /services/file-list.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const fs = require("fs"); 27 | const util = require("util"); 28 | 29 | const readdir = util.promisify(fs.readdir); 30 | 31 | module.exports = async (options) => { 32 | try { 33 | const directoryPath = path.join(process.cwd(), "data", "media"); 34 | const files = await readdir(directoryPath, { withFileTypes: options?.extensions || true }); 35 | const filesDetail = []; 36 | 37 | for (let file of files) { 38 | if (file.name !== ".gitkeep") { 39 | const stat = await fs.promises.stat(path.join(directoryPath, file.name)); 40 | filesDetail.push({ 41 | name: file.name, 42 | size: stat.size, 43 | ctime: stat.ctime, 44 | }); 45 | } 46 | } 47 | 48 | return { files: filesDetail }; 49 | } catch (error) { 50 | logger.warn(`Cannot list files ${error.message}`); 51 | return { errors: [error] }; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /services/file-metadata.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const util = require("util"); 26 | const ffmpeg = require("fluent-ffmpeg"); 27 | const path = require("path"); 28 | 29 | module.exports = async (file) => { 30 | ffmpeg.setFfmpegPath("/root/bin/ffmpeg"); 31 | try { 32 | const ffprobe = util.promisify(ffmpeg.ffprobe); 33 | const metadata = await ffprobe(path.join(__dirname, "..", "data", "media", file)); 34 | return { data: metadata }; 35 | } catch (error) { 36 | logger.warn("Cannot probe media " + error.message); 37 | return { errors: [error] }; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /services/system-info.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const os = require("os"); 27 | const fileReadJson = require("@utils/file-read-json"); 28 | const fileRead = require("@utils/file-read"); 29 | const ffmpegCodecs = require("@utils/ffmpeg-getcodecs"); 30 | const gitCommit = require("@utils/git-commit"); 31 | const nodeVersion = require("@utils/node-version"); 32 | const osVersion = require("@utils/os-version"); 33 | const decklinkVersion = require("@utils/decklink-version"); 34 | 35 | module.exports = async () => { 36 | try { 37 | const packageJson = await fileReadJson(path.join(__dirname, "..", "package.json")); 38 | return { 39 | time: new Date(), 40 | platform: os.platform(), 41 | uptime: os.uptime(), 42 | decklink: await decklinkVersion(), 43 | os: await osVersion(), 44 | ffmpeg: { 45 | commit: await gitCommit(path.resolve("/", "ffmpeg_sources", "ffmpeg")), 46 | version: await fileRead(path.resolve("/", "ffmpeg_sources", "ffmpeg", "RELEASE")), 47 | codecs: await ffmpegCodecs(), 48 | }, 49 | package: { 50 | commit: await gitCommit(path.resolve("/", "home", "node", "app")), 51 | version: packageJson?.version, 52 | name: packageJson?.name, 53 | author: packageJson?.author, 54 | description: packageJson?.description, 55 | license: packageJson?.license, 56 | node: await nodeVersion(), 57 | }, 58 | }; 59 | } catch (error) { 60 | return { error: error.toString() }; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /services/system-job-get.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const jobManager = require("@utils/jobManager"); 26 | 27 | module.exports = async (jobId) => { 28 | try { 29 | const job = jobManager.getbyId(jobId); 30 | return job; 31 | } catch (error) { 32 | logger.warn(error.message); 33 | return { error: error.toString() }; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /services/system-job-getall.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const jobManager = require("@utils/jobManager"); 26 | 27 | module.exports = async () => { 28 | try { 29 | const jobs = await jobManager.getAll(); 30 | return { jobs: jobs }; 31 | } catch (error) { 32 | logger.warn(error.message); 33 | return { error: error.toString() }; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /services/system-job-getthumbnail.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const sharp = require("sharp"); 26 | const path = require("path"); 27 | const thumbnailCache = require("@utils/thumbnail-cache"); 28 | 29 | module.exports = async (jobId, resize = 0.2) => { 30 | try { 31 | const resizedThumbnail = await sharp(path.join(__dirname, "..", "data", "thumbnail", `${jobId}.png`)) 32 | .resize(parseInt(1920 * resize), parseInt(1080 * resize)) 33 | .png() 34 | .toBuffer(); 35 | 36 | await thumbnailCache.set(jobId, resizedThumbnail); 37 | 38 | return `data:image/png;base64,${resizedThumbnail.toString("base64")}`; 39 | } catch (error) { 40 | let fallbackThumbnail = await thumbnailCache.get(jobId); 41 | 42 | if (!fallbackThumbnail) { 43 | fallbackThumbnail = await sharp({ 44 | create: { 45 | width: 48, 46 | height: 48, 47 | channels: 3, 48 | background: { r: 0, g: 0, b: 0 }, 49 | }, 50 | }) 51 | .resize(parseInt(1920 * resize), parseInt(1080 * resize)) 52 | .png() 53 | .toBuffer(); 54 | } 55 | 56 | return `data:image/png;base64,${fallbackThumbnail.toString("base64")}`; 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /services/system-job-kill.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const jobManager = require("@utils/jobManager"); 26 | 27 | module.exports = async (jobId) => { 28 | try { 29 | logger.info(`Killing job ${jobId}`); 30 | const job = jobManager.end(jobId); 31 | return job; 32 | } catch (error) { 33 | logger.warn("Cannot kill job. " + error.message); 34 | return { error: error.toString() }; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /services/system-job-killall.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const jobManager = require("@utils/jobManager"); 26 | 27 | module.exports = async () => { 28 | try { 29 | const jobs = jobManager.getAll(); 30 | 31 | for (let [jobId, job] of Object.entries(jobs)) { 32 | logger.info(`Killing job ${job?.jobId}`); 33 | jobManager.end(job?.jobId); 34 | } 35 | 36 | return jobs; 37 | } catch (error) { 38 | logger.warn(error.message); 39 | return { error: error.toString() }; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /services/system-stats.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const checkDiskSpace = require("check-disk-space").default; 26 | const os = require("os"); 27 | const jobLoad = require("@utils/job-load"); 28 | const path = require("path"); 29 | 30 | const interval = 1000; 31 | let load = 0; 32 | let cpus = {}; 33 | let cpusPrevious = os.cpus(); 34 | let jobs = {}; 35 | 36 | setInterval(async () => { 37 | try { 38 | cpus = os.cpus(); 39 | let loadTotal = 0; 40 | jobs = await jobLoad(); 41 | 42 | for (let i in cpus) { 43 | const usagePrevious = cpusPrevious[i].times?.user + cpusPrevious[i].times?.sys + cpusPrevious[i].times?.irq; 44 | const usage = cpus[i].times?.user + cpus[i].times?.sys + cpus[i].times?.irq; 45 | 46 | const load = (usage - usagePrevious) / interval; 47 | cpus[i].load = Math.round(load * 100) / 100; 48 | 49 | loadTotal += load; 50 | } 51 | 52 | load = Math.round((loadTotal / cpus.length) * 100) / 100; 53 | cpusPrevious = cpus; 54 | } catch (error) {} 55 | }, interval); 56 | 57 | module.exports = async () => { 58 | try { 59 | const disk = await checkDiskSpace(process.env.MEDIA_PATH || path.join(__dirname, "..", "data", "media")); 60 | disk.usage = (disk.size - disk.free) / disk.size; 61 | disk.usage = Math.round(disk.usage * 100) / 100; 62 | 63 | return { 64 | time: new Date(), 65 | cores: cpus.length, 66 | load: load, 67 | cpu: cpus, 68 | env: process.env, 69 | jobs: jobs, 70 | memory: { 71 | total: os.totalmem(), 72 | free: os.freemem(), 73 | usage: Math.round(((os.totalmem - os.freemem()) / os.totalmem) * 100) / 100, 74 | }, 75 | network: os.networkInterfaces(), 76 | platform: os.platform(), 77 | uptime: os.uptime(), 78 | disk: disk, 79 | }; 80 | } catch (error) { 81 | return { error: error.toString() }; 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /services/system-time-set.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const NTP = require("ntp-time").Client; 26 | 27 | module.exports = async (ntpServer = "uk.pool.ntp.org") => { 28 | try { 29 | const client = new NTP(ntpServer, 123, { timeout: 5000 }); 30 | await client.syncTime(); 31 | return { data: client }; 32 | } catch (error) { 33 | logger.warn("Cannot probe media " + error.message); 34 | return { error: error.toString() }; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /services/vmaf-models-get.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const getFilenames = require("@utils/filenames-get"); 26 | 27 | module.exports = async () => { 28 | try { 29 | const data = await getFilenames("/ffmpeg_sources/vmaf/model"); 30 | return { data: data }; 31 | } catch (error) { 32 | logger.warn("Cannot probe media " + error.message); 33 | return { error: error.toString() }; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /services/vmaf-results-csv.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const fs = require("fs"); 27 | const vmafJsonCsv = require("@utils/vmaf-json-csv"); 28 | 29 | const get = async (filePath) => { 30 | try { 31 | const contents = await fs.readFileSync(filePath); 32 | return contents.toString(); 33 | } catch (error) { 34 | logger.warn(error); 35 | return false; 36 | } 37 | }; 38 | 39 | module.exports = async (filename) => { 40 | let csvData = false; 41 | 42 | try { 43 | const dataString = await get(path.join(__dirname, "..", "data", "vmaf", filename)); 44 | const jsonData = JSON.parse(dataString); 45 | csvData = await vmafJsonCsv(jsonData); 46 | } catch (error) { 47 | logger.warn(error); 48 | } 49 | 50 | return csvData; 51 | }; 52 | -------------------------------------------------------------------------------- /services/vmaf-results-json.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const fs = require("fs"); 27 | 28 | const get = async (filePath) => { 29 | try { 30 | const contents = await fs.readFileSync(filePath); 31 | return contents.toString(); 32 | } catch (error) { 33 | logger.warn(error); 34 | return false; 35 | } 36 | }; 37 | 38 | module.exports = async (filename) => { 39 | let data = false; 40 | 41 | try { 42 | const dataString = await get(path.join(__dirname, "..", "data", "vmaf", filename)); 43 | data = JSON.parse(dataString); 44 | } catch (error) { 45 | logger.warn(error); 46 | } 47 | 48 | return data; 49 | }; 50 | -------------------------------------------------------------------------------- /services/zz-decklink-pause.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const ffmpeg = require("fluent-ffmpeg"); 26 | const path = require("path"); 27 | const filterCombine = require("@utils/filter-combine"); 28 | const filterText = require("@utils/filter-text"); 29 | const filterImage = require("@utils/filter-image"); 30 | 31 | let command; 32 | 33 | //ffmpeg -ss 00:00:10 -i input.mp4 -vf "select='eq(n,0)',setpts=N/FRAME_RATE/TB" -r 25 -f decklink 'Decklink Output' 34 | 35 | module.exports = async (cardIndex, options) => { 36 | let status = true; 37 | let repeat = ""; 38 | ffmpeg.setFfmpegPath("/root/bin/ffmpeg"); 39 | 40 | const filters = await filterCombine(await filterText({ ...options, ...job })); 41 | 42 | if (options?.input?.repeat) { 43 | repeat = "-stream_loop -1"; 44 | } 45 | 46 | if (command) { 47 | logger.info("Killing already running FFmpeg process"); 48 | await command.kill(); 49 | } 50 | command = ffmpeg({ logger: logger }) 51 | .input(`${path.join(__dirname, "..", "data", "media", options.file)}`) 52 | .seekInput(options.timestamp) 53 | .complexFilter([ 54 | { 55 | filter: "split", 56 | options: "2", 57 | outputs: ["selected", "dummy"], 58 | }, 59 | { 60 | filter: "trim", 61 | options: "start_frame=0:end_frame=0", 62 | inputs: "selected", 63 | outputs: "output", 64 | }, 65 | { 66 | filter: "setpts", 67 | options: "PTS-STARTPTS", 68 | inputs: "output", 69 | }, 70 | ]) 71 | // .inputOptions([`-re`,repeat]) 72 | // .outputOptions(["-pix_fmt uyvy422","-s 1920x1080","-ac 2","-f decklink"]) 73 | .output(options.cardName) 74 | .outputOptions(["-r 25", "-f decklink", "-format_code 8", "-pix_fmt uyvy422"]); 75 | //.outputOptions(["-pix_fmt uyvy422","-s 1920x1080","-ac 2","-f decklink"]); 76 | 77 | if (Array.isArray(filters)) { 78 | command.videoFilters(filters); 79 | } 80 | 81 | command.on("end", () => { 82 | logger.info("Finished playing file"); 83 | }); 84 | 85 | command.on("error", () => { 86 | logger.info("FFmpeg process killed"); 87 | }); 88 | 89 | command.on("start", (commandString) => { 90 | logger.debug(`Spawned FFmpeg with command: ${commandString}`); 91 | return { options: options, command: commandString }; 92 | }); 93 | 94 | command.on("progress", (progress) => { 95 | logger.info("ffmpeg-progress: " + Math.floor(progress.percent) + "% done"); 96 | }); 97 | 98 | command.on("stderr", (stderrLine) => { 99 | logger.info("ffmpeg: " + stderrLine); 100 | }); 101 | 102 | try { 103 | command.run(); 104 | } catch (error) { 105 | logger.warn(error); 106 | status = "false"; 107 | } 108 | 109 | return { error: status, options: options }; 110 | }; 111 | -------------------------------------------------------------------------------- /services/zz-decklink-stop.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const ffmpeg = require("fluent-ffmpeg"); 26 | const path = require("path"); 27 | 28 | let command; 29 | 30 | module.exports = async (cardIndex, options) => { 31 | logger.info(`Stopping the output on Decklink Device ${cardIndex}.`); 32 | let status = true; 33 | ffmpeg.setFfmpegPath("/root/bin/ffmpeg"); 34 | 35 | command = ffmpeg({ logger: logger }); 36 | command.kill(); 37 | 38 | command.on("error", () => { 39 | logger.info("FFmpeg process killed"); 40 | }); 41 | 42 | command.on("stderr", (stderrLine) => { 43 | logger.debug("Stderr output: " + stderrLine); 44 | }); 45 | 46 | return { error: status, options: options }; 47 | }; 48 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 5 | # Copyright (C) 2022 Ryan McCartney 6 | # 7 | # This file is part of the FFmpeg Docker (ffmpeg-docker). 8 | # 9 | # FFmpeg Docker is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | # Install host dependencies for BMD Decklink 24 | 25 | echo Input the URL for Blackmagic Video Driver URL: 26 | read DEKSTOP_VIDEO_DRIVER_URL 27 | 28 | echo Input the Version for Blackmagic Video Driver: 29 | read DESKTOP_VIDEO_DRIVER_VERSION 30 | 31 | echo Input the Name of the .deb frole for the Blackmagic Video Driver: 32 | read DESKTOP_VIDEO_DRIVER_DEB 33 | 34 | apt update 35 | apt install -y wget dkms dctrl-tools 36 | apt autoremove -y 37 | 38 | #Download and extract 39 | wget -O "desktop-video-driver.tar.gz" "$DEKSTOP_VIDEO_DRIVER_URL" 40 | tar -xvf desktop-video-driver.tar.gz 41 | 42 | #Get .deb name and location 43 | SEARCH_DIR="./Blackmagic_Desktop_Video_Linux_$DESKTOP_VIDEO_DRIVER_VERSION/deb/x86_64/" 44 | echo "Searching for driver in $SEARCH_DIR" 45 | 46 | for FILE in "$SEARCH_DIR"/* 47 | do 48 | echo "$FILE" 49 | if [[ $FILE == *"desktopvideo_"* ]]; then 50 | echo "Found Desktop Video Drivers" 51 | DESKTOP_VIDEO_DRIVER_DEB=$FILE 52 | fi 53 | done 54 | 55 | #Install the .deb 56 | echo "Installing driver from $DESKTOP_VIDEO_DRIVER_DEB" 57 | dpkg --install $DESKTOP_VIDEO_DRIVER_DEB | true 58 | apt install -f -y 59 | dpkg --install $DESKTOP_VIDEO_DRIVER_DEB || true &&\ 60 | 61 | #Cleanup files and folder 62 | rm -r "./Blackmagic_Desktop_Video_Linux_$DESKTOP_VIDEO_DRIVER_VERSION" 63 | rm "desktop-video-driver.tar.gz" 64 | 65 | #Get the firmware info and update if needed 66 | DesktopVideoUpdateTool --list 67 | DesktopVideoUpdateTool --update --all -------------------------------------------------------------------------------- /utils/barsTypes.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | //https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-colorchart_002c-colorspectrum_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc 25 | module.exports = [ 26 | "smptehdbars", 27 | "smptebars", 28 | "rgbtestsrc", 29 | "pal100bars", 30 | "pal75bars", 31 | "pal100bars", 32 | "colorchart", 33 | "testsrc", 34 | "testsrc2", 35 | "yuvtestsrc", 36 | "allrgb", 37 | "allyuv", 38 | ]; 39 | -------------------------------------------------------------------------------- /utils/decklink-version.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { execSync } = require("child_process"); 25 | const { version } = require("os"); 26 | 27 | module.exports = async () => { 28 | let decklinkObject = {}; 29 | const decklink = await execSync(`dpkg -l | grep -i blackmagic`).toString().trim(); 30 | 31 | if (decklink) { 32 | const items = decklink.split(" "); 33 | decklinkObject.version = items[2].trim(); 34 | decklinkObject.arch = items[5].trim(); 35 | decklinkObject.name = items[6].trim(); 36 | } 37 | 38 | return decklinkObject; 39 | }; 40 | -------------------------------------------------------------------------------- /utils/decklinkFormats.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = [ 25 | { 26 | rawFormat: "auto", 27 | description: 28 | "This is the default which means 8-bit YUV 422 or 8-bit ARGB if format autodetection is used, 8-bit YUV 422 otherwise.", 29 | }, 30 | { 31 | rawFormat: "uyvy422", 32 | description: "8-bit YUV 422", 33 | }, 34 | { 35 | rawFormat: "yuv422p10", 36 | description: "10-bit YUV 422", 37 | }, 38 | { 39 | rawFormat: "argb", 40 | description: "8-bit RGB", 41 | }, 42 | { 43 | rawFormat: "bgra", 44 | description: "8-bit RGB", 45 | }, 46 | { 47 | rawFormat: "rgb10", 48 | description: "10-bit RGB", 49 | }, 50 | ]; 51 | -------------------------------------------------------------------------------- /utils/documentation.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const express = require("express"); 25 | const documentation = express.Router(); 26 | 27 | const swaggerUi = require("swagger-ui-express"); 28 | const swaggerJSDoc = require("swagger-jsdoc"); 29 | 30 | const host = process.env.HOST || "localhost"; 31 | const port = process.env.PORT || "80"; 32 | const url = `http://${host}:${port}/api/`; 33 | 34 | const swaggerOptions = { 35 | definition: { 36 | openapi: "3.0.0", 37 | info: { 38 | title: "FFmpeg Docker API", 39 | version: "0.1.0", 40 | description: "Common FFmpeg functions from a RESTful API", 41 | license: { 42 | name: "GPLv3", 43 | url: "https://www.gnu.org/licenses/gpl-3.0.en.html", 44 | }, 45 | contact: { 46 | name: "Ryan McCartney", 47 | url: "https://ryan.mccartney.info/ffmpeg-docker", 48 | email: "ryan@mccartney.info", 49 | }, 50 | }, 51 | servers: [ 52 | { 53 | url: url, 54 | }, 55 | ], 56 | }, 57 | apis: ["./routes/*.js", "./modules/*.js"], 58 | }; 59 | 60 | const swaggerDocs = swaggerJSDoc(swaggerOptions); 61 | documentation.use("/", swaggerUi.serve, swaggerUi.setup(swaggerDocs)); 62 | 63 | module.exports = documentation; 64 | -------------------------------------------------------------------------------- /utils/encodePresets.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow"]; 25 | -------------------------------------------------------------------------------- /utils/ffconcat.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const fs = require("fs"); 25 | const path = require("path"); 26 | const logger = require("@utils/logger")(module); 27 | 28 | const parse = async (data) => { 29 | try { 30 | const lines = data.split("\n"); 31 | const items = []; 32 | for (let line of lines) { 33 | if (line && line[0] !== "#") { 34 | const lineParts = line.split("/"); 35 | items.push(lineParts[lineParts.length - 1]); 36 | } 37 | } 38 | return items; 39 | } catch (error) { 40 | logger.warn(error); 41 | return false; 42 | } 43 | }; 44 | 45 | const get = async (playlist = "playlist") => { 46 | try { 47 | const playlistFilePath = path.join(__dirname, "..", "data", "playlist", `${playlist}.ffconcat`); 48 | const contents = await fs.readFileSync(playlistFilePath); 49 | return contents.toString(); 50 | } catch (error) { 51 | logger.warn(error); 52 | return false; 53 | } 54 | }; 55 | 56 | const set = async (playlist = "playlist", items = []) => { 57 | try { 58 | const playlistFilePath = path.join(__dirname, "..", "data", "playlist", `${playlist}.ffconcat`); 59 | let data = `# Playlist Name: ${playlist}.ffconcat`; 60 | 61 | for (let item of items) { 62 | data += `\n\n# File Name: ${item}\n${path.join(__dirname, "..", "data", "media", item)}`; 63 | } 64 | 65 | await fs.writeFileSync(playlistFilePath, data); 66 | 67 | return { data: { raw: data, items: items } }; 68 | } catch (error) { 69 | logger.warn(error); 70 | return false; 71 | } 72 | }; 73 | 74 | const add = async (playlist = "playlist", item) => { 75 | try { 76 | let data = await get(playlist); 77 | 78 | if (item) { 79 | data += `\n\n# File Name: ${item}\n${path.join(__dirname, "..", "data", "media", item)}`; 80 | } 81 | 82 | const items = await parse(data); 83 | const setData = await set(playlist, items); 84 | 85 | return { data: { raw: data, items: items } }; 86 | } catch (error) { 87 | logger.warn(error); 88 | return false; 89 | } 90 | }; 91 | 92 | const remove = async (playlist = "playlist", item) => { 93 | try { 94 | const itemPath = path.join(__dirname, "..", "data", "media", item); 95 | const data = await get(playlist); 96 | const lines = data.split("\n"); 97 | const items = []; 98 | 99 | for (let line of lines) { 100 | if (line && line[0] !== "#" && line !== itemPath) { 101 | const lineParts = line.split("/"); 102 | items.push(lineParts[lineParts.length - 1]); 103 | } 104 | } 105 | 106 | const setData = await set(playlist, items); 107 | return { data: items, raw: setData.data.raw }; 108 | } catch (error) { 109 | logger.warn(error); 110 | return false; 111 | } 112 | }; 113 | 114 | const getItems = async (playlist = "playlist") => { 115 | try { 116 | const data = await get(playlist); 117 | const items = await parse(data); 118 | return { data: { items: items, raw: data } }; 119 | } catch (error) { 120 | logger.warn(error); 121 | return false; 122 | } 123 | }; 124 | 125 | module.exports = { getItems: getItems, set: set, add: add, remove: remove }; 126 | -------------------------------------------------------------------------------- /utils/ffmpeg-getcodecs.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const ffmpeg = require("fluent-ffmpeg"); 25 | const util = require("util"); 26 | const logger = require("@utils/logger")(module); 27 | 28 | module.exports = async (relativePath) => { 29 | ffmpeg.setFfmpegPath("/root/bin/ffmpeg"); 30 | const getAvailableCodecs = util.promisify(ffmpeg.getAvailableCodecs); 31 | 32 | try { 33 | const codecs = await getAvailableCodecs(); 34 | return codecs; 35 | } catch (error) { 36 | logger.warn(error); 37 | return {}; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /utils/file-delete.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const fs = require("fs").promises; 25 | const path = require("path"); 26 | const logger = require("@utils/logger")(module); 27 | 28 | module.exports = async (relativePath) => { 29 | try { 30 | const absolutePath = path.resolve(relativePath); 31 | if (await fs.unlink(absolutePath)) { 32 | return true; 33 | } 34 | return false; 35 | } catch (error) { 36 | logger.warn(error); 37 | return false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /utils/file-exists.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const fs = require("fs"); 25 | const path = require("path"); 26 | const logger = require("@utils/logger")(module); 27 | 28 | module.exports = async (relativePath) => { 29 | try { 30 | const absolutePath = path.resolve(relativePath); 31 | if (await fs.existsSync(absolutePath)) { 32 | return true; 33 | } 34 | return false; 35 | } catch (error) { 36 | logger.warn(error); 37 | return false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /utils/file-read-json.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { promises: fs } = require("fs"); 25 | 26 | module.exports = async (filename) => { 27 | const fileJson = await fs.readFile(filename); 28 | return JSON.parse(fileJson); 29 | }; 30 | -------------------------------------------------------------------------------- /utils/file-read.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { promises: fs } = require("fs"); 25 | 26 | module.exports = async (file) => { 27 | const fileContents = await fs.readFile(file); 28 | return fileContents.toString().trim(); 29 | }; 30 | -------------------------------------------------------------------------------- /utils/file-upload.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const path = require("path"); 25 | const multer = require("multer"); 26 | 27 | const storage = multer.diskStorage({ 28 | destination: (req, file, cb) => { 29 | cb(null, path.join(__dirname, "..", "data", "media")); 30 | }, 31 | filename: (req, file, cb) => { 32 | let extArray = file.mimetype.split("/"); 33 | let extension = extArray[extArray.length - 1]; 34 | cb(null, `${file.originalname.split(".")[0]}.${extension}`); 35 | }, 36 | }); 37 | 38 | module.exports = multer({ 39 | storage: storage, 40 | }).single("file"); 41 | -------------------------------------------------------------------------------- /utils/filenames-get.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const fs = require("fs"); 26 | const fsp = fs.promises; 27 | const path = require("path"); 28 | 29 | module.exports = async (dirname) => { 30 | let files = []; 31 | try { 32 | files = await fsp.readdir(path.resolve(dirname)); 33 | return files; 34 | } catch (error) { 35 | logger.warn(error); 36 | return files; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /utils/filter-audio-meter.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | 27 | module.exports = async (options = {}) => { 28 | let filters = []; 29 | try { 30 | if (options.audioMeter) { 31 | filters = [ 32 | { 33 | filter: "amovie", 34 | options: "1:d=0", 35 | outputs: "volume", 36 | }, 37 | { 38 | filter: "showvolume", 39 | inputs: "volume", 40 | outputs: "volume_info", 41 | }, 42 | { 43 | filter: "drawtext", 44 | options: 45 | "text='Volume: %{metadata=lavfi.showvolume.volume': x=(w-tw-10): y=(h-th-10): fontcolor=white: fontsize=24: box=1: boxcolor=black'", 46 | inputs: "volume_info", 47 | outputs: "output", 48 | }, 49 | ]; 50 | } 51 | } catch (error) { 52 | logger.warn("Cannot create text filter " + error.message); 53 | } 54 | return filters; 55 | }; 56 | -------------------------------------------------------------------------------- /utils/filter-combine.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | 26 | module.exports = async (...filter) => { 27 | let filters = []; 28 | try { 29 | const length = filter.length; 30 | for (let i = 0; i < length; i += 1) { 31 | if (Array.isArray(filter[i])) { 32 | filters = filters.concat(filter[i]); 33 | } 34 | } 35 | } catch (error) { 36 | logger.warn("Cannot concatenate filters " + error.message); 37 | } 38 | return filters; 39 | }; 40 | -------------------------------------------------------------------------------- /utils/filter-image.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const getFilepath = require("@utils/get-filepath"); 26 | 27 | module.exports = async (command, options) => { 28 | try { 29 | if (options.overlay.image && options.overlay.image.file) { 30 | const filepath = await getFilepath({ 31 | file: options.overlay.image.file, 32 | timestamp: false, 33 | format: options.overlay.image.format || "png", 34 | }); 35 | 36 | //ffmpeg -y -i video.mp4 -i overlay.png -filter_complex [0]overlay=x=0:y=0[out] -map [out] -map 0:a? test.mp4 37 | 38 | // command.input(filepath); 39 | // command.complexFilter( 40 | // [ 41 | // { 42 | // filter: "overlay", 43 | // options: { x: 0, y: 0 }, 44 | // inputs: ["0:v"], 45 | // outputs: "output", 46 | // }, 47 | // ], 48 | // "output" 49 | // ); 50 | } 51 | } catch (error) { 52 | logger.warn("Cannot create image overlay filter"); 53 | logger.warn(error); 54 | } 55 | return command; 56 | }; 57 | -------------------------------------------------------------------------------- /utils/filter-interlace.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const parse = require("@utils/parse"); 27 | 28 | module.exports = async (interlace = false, fieldorder = "tff") => { 29 | const filters = []; 30 | try { 31 | if (interlace) { 32 | filters.push({ 33 | filter: `yadif`, 34 | // options: { mode: 1, parity: "t", deint: 0 }, 35 | }); 36 | } 37 | } catch (error) { 38 | logger.warn("Cannot create interalcing filter " + error.message); 39 | } 40 | return filters; 41 | }; 42 | -------------------------------------------------------------------------------- /utils/filter-qr.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const qr = require("@utils/qr"); 27 | const imageFilter = require("@utils/filter-image"); 28 | 29 | module.exports = async (command, options) => { 30 | try { 31 | if (options.overlay.qr && options.overlay.qr.data) { 32 | const data = await qr(options.overlay.qr); 33 | 34 | options.overlay.image.file = path.join(__dirname, "..", "data", "qr", qrData?.file); 35 | options.overlay.image.format = options.overlay.qr?.type || "png"; 36 | options.overlay.image.size = options.overlay.qr?.size || 20; 37 | options.overlay.image.location = { 38 | x: options?.overlay?.qr?.location?.x || 0, 39 | y: options?.overlay?.qr?.location?.y || 0, 40 | }; 41 | 42 | command = imageFilter(command, options); 43 | } 44 | } catch (error) { 45 | logger.warn("Cannot create QR code filter "); 46 | logger.warn(error); 47 | } 48 | return command; 49 | }; 50 | -------------------------------------------------------------------------------- /utils/get-extension.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (format = "h264") => { 25 | if (format === "prores") { 26 | return ".mov"; 27 | } 28 | 29 | if (format === "h264") { 30 | return ".mp4"; 31 | } 32 | 33 | if (format === "mjpeg") { 34 | return ".mov"; 35 | } 36 | 37 | return `.${format}`; 38 | }; 39 | -------------------------------------------------------------------------------- /utils/get-filepath.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const path = require("path"); 26 | const { format } = require("date-fns"); 27 | const getFileExtension = require("@utils/get-extension"); 28 | 29 | const defaultOptions = { 30 | format: "h264", 31 | includePath: true, 32 | format: "h264", 33 | timestamp: "MM-dd-yyyy-HH-mm", 34 | chunks: false, 35 | }; 36 | 37 | module.exports = async (options = {}) => { 38 | options = { ...defaultOptions, ...options }; 39 | 40 | let filePath = ""; 41 | let mediaPath = ""; 42 | 43 | if (options?.includePath) { 44 | mediaPath = process.env.MEDIA_PATH || path.join(__dirname, "..", "data", "media"); 45 | } 46 | 47 | if (options?.chunks && options?.timestamp) { 48 | const dateTimeString = format(new Date(), options?.timestamp); 49 | filePath = path.join(mediaPath, `${options?.file}-${dateTimeString}-%03d${getFileExtension(options?.format)}`); 50 | } else if (options?.chunks) { 51 | filePath = path.join(mediaPath, `${options?.file}-%03d${getFileExtension(options?.format)}`); 52 | } else if (options?.timestamp) { 53 | const dateTimeString = format(new Date(), options?.timestamp); 54 | filePath = path.join(mediaPath, `${options?.file}-${dateTimeString}${getFileExtension(options?.format)}`); 55 | } else { 56 | filePath = path.join(mediaPath, `${options?.file}${getFileExtension(options?.format)}`); 57 | } 58 | 59 | return filePath; 60 | }; 61 | -------------------------------------------------------------------------------- /utils/git-commit.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { execSync } = require("child_process"); 25 | 26 | module.exports = async (directory) => { 27 | const revision = await execSync( 28 | `cd ${directory} && git config --global --add safe.directory ${directory} && git rev-parse HEAD` 29 | ) 30 | .toString() 31 | .trim(); 32 | return revision; 33 | }; 34 | -------------------------------------------------------------------------------- /utils/hash-response.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const md5 = require("md5"); 25 | 26 | module.exports = (res, req, contents) => { 27 | const response = contents; 28 | const meta = { 29 | hash: md5(response), 30 | request_url: `${req.protocol}://${req.hostname}${req.originalUrl}`, 31 | request_method: req.method, 32 | request_body: req.body, 33 | user: req.user, 34 | }; 35 | 36 | response.meta = meta; 37 | res.header("Content-Type", "application/json"); 38 | res.json(response); 39 | }; 40 | -------------------------------------------------------------------------------- /utils/http-logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const morgan = require("morgan"); 25 | const logger = require("@utils/logger")(module); 26 | 27 | logger.stream = { 28 | write: (message) => { 29 | message = message.substring(0, message.lastIndexOf("\n")); 30 | const items = message.split(" "); 31 | logger.http(message, { remoteAddress: items[0] }); 32 | }, 33 | }; 34 | 35 | module.exports = morgan(`:remote-addr :method :url :status :res[content-length] :response-time ms`, { 36 | stream: logger.stream, 37 | }); 38 | -------------------------------------------------------------------------------- /utils/job-load.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const jobManager = require("@utils/jobManager"); 26 | const pidusage = require("pidusage"); 27 | 28 | module.exports = async () => { 29 | let usage = {}; 30 | const pids = []; 31 | const jobs = jobManager.getAll(); 32 | 33 | for (let [jobId, job] of Object.entries(jobs)) { 34 | pids.push(job?.pid); 35 | } 36 | 37 | if (pids.length > 0) { 38 | usage = await pidusage(pids); 39 | } 40 | 41 | for (let [jobId, job] of Object.entries(jobs)) { 42 | usage[jobId] = usage[job?.pid]; 43 | usage[jobId].cpu = usage[jobId].cpu / 100; 44 | jobManager.update(jobId, { load: usage[job?.pid] }); 45 | delete usage[job?.pid]; 46 | } 47 | 48 | return usage; 49 | }; 50 | -------------------------------------------------------------------------------- /utils/jobManager.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const crypto = require("crypto"); 25 | const fileDelete = require("@utils/file-delete"); 26 | const thumbnailCache = require("@utils/thumbnail-cache"); 27 | 28 | let maxQueueSize = process.env.QUEUE_SIZE || 5; 29 | let jobs = {}; 30 | 31 | const start = (output, name = "FFmpeg Process", type = ["default"]) => { 32 | const queueSize = Object.keys(jobs).length; 33 | if (queueSize < maxQueueSize) { 34 | const hash = crypto.createHash("md5").update(output).digest("hex"); 35 | if (!jobs[hash]) { 36 | jobs[hash] = { 37 | jobNumber: Object.keys(jobs).length + 1, 38 | jobId: hash, 39 | started: new Date(), 40 | jobName: name, 41 | type: type, 42 | }; 43 | return jobs[hash]; 44 | } else { 45 | throw new Error("Job is already running."); 46 | } 47 | } else { 48 | throw new Error("Job queue is full, cancel a job or wait to one is finished before starting another."); 49 | } 50 | }; 51 | 52 | const update = (hash, update) => { 53 | if (jobs[hash]) { 54 | jobs[hash] = { ...jobs[hash], ...update }; 55 | return jobs[hash]; 56 | } else { 57 | return { error: "Job does not exist." }; 58 | } 59 | }; 60 | 61 | const end = (hash, kill = true) => { 62 | if (jobs[hash]) { 63 | const job = jobs[hash]; 64 | 65 | if (kill) { 66 | process.kill(jobs[hash].pid, "SIGINT"); 67 | } 68 | 69 | job.ended = new Date(); 70 | job.duration = job.ended - job.started; 71 | delete jobs[hash]; 72 | 73 | fileDelete(`data/thumbnail/${job.jobId}.png`); 74 | thumbnailCache.del(job.jobId); 75 | 76 | return job; 77 | } else { 78 | return { error: "Job does not exist." }; 79 | } 80 | }; 81 | 82 | const get = (output) => { 83 | const hash = crypto.createHash("md5").update(output).digest("hex"); 84 | if (jobs[hash]) { 85 | return jobs[hash]; 86 | } else { 87 | return false; 88 | } 89 | }; 90 | 91 | const getbyId = (hash) => { 92 | if (jobs[hash]) { 93 | return jobs[hash]; 94 | } else { 95 | return false; 96 | } 97 | }; 98 | 99 | const getAll = () => { 100 | return jobs; 101 | }; 102 | 103 | module.exports = { end: end, update: update, start: start, get: get, getbyId: getbyId, getAll: getAll }; 104 | -------------------------------------------------------------------------------- /utils/logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const winston = require("winston"); 25 | const path = require("path"); 26 | require("winston-daily-rotate-file"); 27 | const logFolder = process.env.LOG_FOLDER || "logs"; 28 | const logName = process.env.LOG_NAME || "ffmpeg"; 29 | const logLevel = process.env.LOG_LEVEL || "info"; 30 | 31 | const customLevels = { 32 | levels: { 33 | error: 0, 34 | warn: 1, 35 | info: 2, 36 | http: 3, 37 | debug: 4, 38 | }, 39 | colors: { 40 | error: "red", 41 | warn: "yellow", 42 | info: "blue", 43 | http: "magenta", 44 | debug: "gray", 45 | }, 46 | }; 47 | 48 | const customLogFormat = winston.format.combine( 49 | winston.format.errors({ stack: true }), 50 | winston.format.timestamp(), 51 | winston.format.splat(), 52 | winston.format.printf((log) => `${log.timestamp} ${log.level}: ${log.message}`) 53 | ); 54 | 55 | winston.addColors(customLevels.colors); 56 | 57 | const loggerInstance = winston.createLogger({ 58 | levels: customLevels.levels, 59 | handleExceptions: false, 60 | transports: [ 61 | new winston.transports.DailyRotateFile({ 62 | level: logLevel, 63 | format: customLogFormat, 64 | filename: path.join(logFolder, logName + "-%DATE%.log"), 65 | datePattern: "YYYY-MM-DD", 66 | zippedArchive: true, 67 | maxSize: "100m", 68 | maxFiles: "1d", 69 | }), 70 | new winston.transports.Console({ 71 | level: logLevel, 72 | handleExceptions: true, 73 | colorize: true, 74 | format: winston.format.combine(customLogFormat, winston.format.colorize({ all: true })), 75 | }), 76 | ], 77 | }); 78 | 79 | const logger = (module) => { 80 | const filename = path.basename(module.filename); 81 | const loggers = {}; 82 | 83 | for (let level in customLevels.levels) { 84 | loggers[level] = (message, metadata) => { 85 | loggerInstance[level](message, { metadata: { ...{ filename: filename }, ...metadata } }); 86 | }; 87 | } 88 | 89 | return loggers; 90 | }; 91 | 92 | module.exports = logger; 93 | -------------------------------------------------------------------------------- /utils/markdown.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const swaggerJsdoc = require("swagger-jsdoc"); 25 | const YAML = require("yaml"); 26 | const fs = require("fs"); 27 | const path = require("path"); 28 | 29 | const host = process.env.HOST || "localhost"; 30 | const port = process.env.PORT || "80"; 31 | const url = `http://${host}:${port}/api/`; 32 | 33 | const options = { 34 | definition: { 35 | openapi: "3.0.0", 36 | info: { 37 | title: "FFmpeg Docker API", 38 | version: "0.1.0", 39 | description: "Common FFmpeg functions from a RESTful API", 40 | license: { 41 | name: "GPLv3", 42 | url: "https://www.gnu.org/licenses/gpl-3.0.en.html", 43 | }, 44 | contact: { 45 | name: "Ryan McCartney", 46 | url: "https://ryan.mccartney.info/ffmpeg-docker", 47 | email: "ryan@mccartney.info", 48 | }, 49 | }, 50 | servers: [ 51 | { 52 | url: url, 53 | }, 54 | ], 55 | }, 56 | apis: ["./routes/*.js", "./modules/*.js"], 57 | }; 58 | 59 | const main = async () => { 60 | const swaggerJsonSpec = swaggerJsdoc(options); 61 | const swaggerYamlSpec = YAML.stringify(swaggerJsonSpec); 62 | fs.writeFileSync(path.join("docs", "assets", "api-spec.yml"), swaggerYamlSpec); 63 | }; 64 | 65 | main(); 66 | -------------------------------------------------------------------------------- /utils/node-version.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { execSync } = require("child_process"); 25 | 26 | module.exports = async () => { 27 | const revision = await execSync(`node --version`).toString().trim(); 28 | return revision; 29 | }; 30 | -------------------------------------------------------------------------------- /utils/os-version.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const { execSync } = require("child_process"); 25 | 26 | module.exports = async () => { 27 | const osObject = {}; 28 | const os = await execSync(`cat /etc/os-release`).toString().trim(); 29 | 30 | for (let line of os.split("\n")) { 31 | const item = line.split("="); 32 | osObject[item[0].toLowerCase()] = item[1].replace(/"/g, ``).trim(); 33 | } 34 | 35 | return osObject; 36 | }; 37 | -------------------------------------------------------------------------------- /utils/parse.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const Mustache = require("mustache"); 26 | 27 | const host = process.env.HOST || "localhost"; 28 | const queueSize = process.env.QUEUE_SIZE || "10"; 29 | 30 | const port = process.env.PORT || "80"; 31 | 32 | module.exports = async (inputString, options = {}) => { 33 | let parsedString = ""; 34 | try { 35 | const view = { 36 | ...{ 37 | date: new Date(), 38 | host: host, 39 | port: port, 40 | queueSize: queueSize.toString(), 41 | }, 42 | ...options, 43 | }; 44 | 45 | parsedString = await Mustache.render(inputString, view); 46 | } catch (error) { 47 | logger.warn(error); 48 | } 49 | return parsedString; 50 | }; 51 | -------------------------------------------------------------------------------- /utils/qr.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | const util = require("util"); 26 | const path = require("path"); 27 | const QRCode = require("qrcode"); 28 | const parse = require("@utils/parse"); 29 | 30 | module.exports = async (qr) => { 31 | const response = { status: true, data: {} }; 32 | try { 33 | const QrType = qr?.type || "png"; 34 | const qrCodePath = path.join(__dirname, "..", "data", "qr", qr?.file + "." + QrType); 35 | 36 | response.data = await QRCode.toFile(qrCodePath, await parse(qr?.text, qr), { 37 | type: QrType, 38 | color: { 39 | dark: `${qr.color || "#FFF"}`, 40 | light: `${qr.background || "#000"}`, 41 | }, 42 | }); 43 | } catch (error) { 44 | logger.warn(error); 45 | response.status = false; 46 | response.error = error; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /utils/rtmp-address.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (address, path = "", key) => { 25 | let fullAddress = `rtmp://${address}${path}`; 26 | if (key) { 27 | fullAddress += `/${key}`; 28 | } 29 | return fullAddress; 30 | }; 31 | -------------------------------------------------------------------------------- /utils/set-codec.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const defaultOutput = { audioCodec: "aac", videoCodec: "h264", encodePreset: "ultrafast" }; 25 | 26 | module.exports = (command, output = {}) => { 27 | output = { ...defaultOutput, ...output }; 28 | 29 | if (output?.videoCodec === "prores") { 30 | command.videoCodec("prores_ks").outputOptions("-profile:v", "3").outputOptions("-c:a", "pcm_s16le"); 31 | } 32 | 33 | if (output?.videoCodec === "h264") { 34 | command 35 | .videoCodec("libx264") 36 | .outputOptions("-preset", output?.encodePreset || "ultrafast") 37 | .outputOptions("-pass", "1") 38 | .outputOptions("-tune zerolatency") 39 | .outputOptions("-max_delay", "100"); 40 | } 41 | 42 | if (output?.videoCodec === "mjpeg") { 43 | command 44 | .videoCodec("mjpeg") 45 | .outputOptions("-q:v", "10") 46 | .outputOptions("-c:a", "copy") 47 | .addOutputOptions("-pix_fmt", "yuvj422p"); 48 | } 49 | 50 | if (output?.videoCodec === "mjpeg2") { 51 | command.videoCodec("mpeg2video").addOutputOptions("-pix_fmt", "yuv420p"); 52 | } 53 | 54 | if (!output?.videoCodec) { 55 | command 56 | .videoCodec("libx264") 57 | .videoCodec("libx264") 58 | .outputOptions("-crf", "23") 59 | .outputOptions("-preset", output?.encodePreset || "ultrafast"); 60 | } 61 | 62 | if (output?.audioCodec === "aac") { 63 | command.audioCodec("aac"); 64 | } 65 | 66 | if (output?.audioCodec === "mp3") { 67 | command.audioCodec("mp3"); 68 | } 69 | 70 | if (!output?.audioCodec) { 71 | command.audioCodec("mp3"); 72 | } 73 | 74 | return command; 75 | }; 76 | -------------------------------------------------------------------------------- /utils/strategies.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const passport = require("passport"); 25 | const logger = require("@utils/logger")(module); 26 | const JwtStrategy = require("passport-jwt").Strategy; 27 | const ExtractJwt = require("passport-jwt").ExtractJwt; 28 | 29 | const jwtStrategy = (settings) => { 30 | const options = { 31 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 32 | secretOrKey: process.env.AUTH_KEY || "averysecretkey", 33 | issuer: "accounts.examplesoft.com", 34 | audience: "yoursite.net", 35 | }; 36 | 37 | return new JwtStrategy(options, function (jwt_payload, done) { 38 | User.findOne({ id: jwt_payload.sub }, function (err, user) { 39 | if (err) { 40 | return done(err, false); 41 | } 42 | if (user) { 43 | return done(null, user); 44 | } else { 45 | return done(null, false); 46 | } 47 | }); 48 | }); 49 | }; 50 | 51 | module.exports = { 52 | local: jwtStrategy, 53 | }; 54 | -------------------------------------------------------------------------------- /utils/thumbnail-cache.js: -------------------------------------------------------------------------------- 1 | const NodeCache = require("node-cache"); 2 | const thumbnailCache = new NodeCache({ stdTTL: 100, checkperiod: 120 }); 3 | 4 | module.exports = thumbnailCache; 5 | -------------------------------------------------------------------------------- /utils/validator-isiporfqdn.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const validator = require("validator"); 25 | 26 | module.exports = (value, { req, location, path }) => { 27 | return validator.isIP(value) || validator.isFQDN(value); 28 | }; 29 | -------------------------------------------------------------------------------- /utils/vmaf-json-csv.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const logger = require("@utils/logger")(module); 25 | 26 | // "frameNum": 0, 27 | // "metrics": { 28 | // "integer_adm2": 0.972270, 29 | // "integer_adm_scale0": 0.970884, 30 | // "integer_adm_scale1": 0.964849, 31 | // "integer_adm_scale2": 0.977850, 32 | // "integer_adm_scale3": 0.971833, 33 | // "integer_motion2": 0.000000, 34 | // "integer_motion": 0.000000, 35 | // "integer_vif_scale0": 0.570051, 36 | // "integer_vif_scale1": 0.889042, 37 | // "integer_vif_scale2": 0.930582, 38 | // "integer_vif_scale3": 0.952735, 39 | // "psnr": 37.205054, 40 | // "ssim": 0.987119, 41 | // "ms_ssim": 0.984290, 42 | // "vmaf": 84.659084 43 | 44 | module.exports = async (json = { frames: [] }) => { 45 | let csvData = "Frame Number,PSNR,SSIM,VMAF\n"; 46 | try { 47 | for (let frame of json?.frames) { 48 | const csvLine = `${frame?.frameNum},${frame?.metrics?.psnr},${frame?.metrics?.ssim},${frame?.metrics?.vmaf}\n`; 49 | csvData += csvLine; 50 | } 51 | } catch (error) { 52 | logger.warn(error); 53 | } 54 | return csvData; 55 | }; 56 | -------------------------------------------------------------------------------- /utils/watcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | fs.watch("/path/to/folder", (eventType, filename) => { 25 | console.log(eventType); 26 | // could be either 'rename' or 'change'. new file event and delete 27 | // also generally emit 'rename' 28 | console.log(filename); 29 | }); 30 | -------------------------------------------------------------------------------- /validators/audio.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const barsType = require("@utils/barsTypes"); 25 | 26 | module.exports = (direction = "input") => { 27 | return { 28 | [`${direction}.type`]: { 29 | optional: true, 30 | isIn: { 31 | options: [barsType], 32 | default: barsType[0], 33 | errorMessage: `Bars type must be one of ${barsType.toString()}`, 34 | }, 35 | }, 36 | [`${direction}.file`]: { 37 | exists: { 38 | errorMessage: "Input filename required.", 39 | }, 40 | isString: { errorMessage: "Filename must be a string." }, 41 | }, 42 | [`${direction}.repeat`]: { 43 | optional: true, 44 | isBoolean: { default: false, errorMessage: "Repear must be a boolean value." }, 45 | }, 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /validators/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = { 25 | username: { 26 | exists: { 27 | errorMessage: "Username is required.", 28 | }, 29 | isString: { errorMessage: "Username must be a string" }, 30 | }, 31 | password: { 32 | exists: { 33 | errorMessage: "Password is required.", 34 | }, 35 | isString: { errorMessage: "Password must be a string" }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /validators/bars.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const barsType = require("@utils/barsTypes"); 25 | 26 | module.exports = (direction = "input") => { 27 | return { 28 | [`${direction}.type`]: { 29 | optional: true, 30 | isIn: { 31 | options: [barsType], 32 | default: barsType[0], 33 | errorMessage: `Bars type must be one of ${barsType.toString()}`, 34 | }, 35 | }, 36 | [`${direction}.frequency`]: { 37 | optional: true, 38 | isInt: { 39 | min: 100, 40 | max: 18000, 41 | default: 1000, 42 | errorMessage: "Audio frequency must be between 100Hz and 18,000 Hz", 43 | }, 44 | }, 45 | [`${direction}.duration`]: { 46 | optional: true, 47 | isInt: { 48 | min: 1, 49 | max: 3600, 50 | default: 10, 51 | errorMessage: "Duration of bars must be between 1 and 3,600 seconds as an integar", 52 | }, 53 | }, 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /validators/decklink.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | 26 | module.exports = (direction = "input") => { 27 | return { 28 | [`${direction}.cardName`]: { 29 | exists: { 30 | errorMessage: "Decklink card name required", 31 | }, 32 | isString: { errorMessage: "Decklink card name must be a string" }, 33 | }, 34 | [`${direction}.volume`]: { 35 | optional: true, 36 | isFloat: { min: 0, max: 1, default: 0.25, errorMessage: "Volume must be a float between 0 and 1" }, 37 | }, 38 | [`${direction}.duplexMode`]: { 39 | optional: true, 40 | isIn: { 41 | options: [["full", "half"]], 42 | default: "unset", 43 | errorMessage: "Decklink card name must one of 'full', 'half' or 'unset'", 44 | }, 45 | }, 46 | [`${direction}.encodePreset`]: { 47 | optional: true, 48 | isIn: { 49 | options: [encodePresets], 50 | default: encodePresets[0], 51 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 52 | }, 53 | }, 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /validators/file.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (direction = "input") => { 25 | const validator = { 26 | [`${direction}.file`]: { 27 | isString: { errorMessage: "Filename must be a string." }, 28 | }, 29 | [`${direction}.repeat`]: { 30 | optional: true, 31 | isBoolean: { default: false, errorMessage: "Repear must be a boolean value." }, 32 | }, 33 | [`${direction}.chunkSize`]: { 34 | optional: true, 35 | isInt: { min: 5, max: 1800, errorMessage: "Chunks must be between 5 and 1800 seconds." }, 36 | }, 37 | }; 38 | 39 | if (direction === "output") { 40 | validator[`${direction}.file`].optional = true; 41 | } else { 42 | validator[`${direction}.file`].exists = { 43 | errorMessage: "Input filename required.", 44 | }; 45 | } 46 | 47 | return validator; 48 | }; 49 | -------------------------------------------------------------------------------- /validators/hls.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | 26 | module.exports = (direction = "input") => { 27 | return { 28 | [`${direction}.file`]: { 29 | optional: true, 30 | isString: { errorMessage: "HLS filename must be a string" }, 31 | }, 32 | [`${direction}.chunkDuration`]: { 33 | optional: true, 34 | isFloat: { 35 | min: 0, 36 | max: 10, 37 | default: 0.5, 38 | errorMessage: "Chunk duration must be a float between 0 and 10 seconds", 39 | }, 40 | }, 41 | [`${direction}.chunks`]: { 42 | optional: true, 43 | isInt: { 44 | min: 1, 45 | max: 50, 46 | default: 5, 47 | errorMessage: "Number of chunks must be an integar between 1 and 50", 48 | }, 49 | }, 50 | [`${direction}.encodePreset`]: { 51 | optional: true, 52 | isIn: { 53 | options: [encodePresets], 54 | default: encodePresets[0], 55 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 56 | }, 57 | }, 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /validators/overlay.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (direction = "input") => { 25 | return { 26 | "overlay.line1": { 27 | optional: true, 28 | isString: { default: "", errorMessage: "Line 1 must be a String" }, 29 | }, 30 | "overlay.line2": { 31 | optional: true, 32 | isString: { default: "", errorMessage: "Line 2 must be a String" }, 33 | }, 34 | "overlay.fontSize": { 35 | optional: true, 36 | isInt: { min: 10, max: 200, default: 120, errorMessage: "Font size must be between 10 and 200" }, 37 | }, 38 | "overlay.timecode": { 39 | optional: true, 40 | isBoolean: { default: false, errorMessage: "Timecode must be a Boolean value" }, 41 | }, 42 | "overlay.offset": { 43 | optional: true, 44 | isInt: { min: -12, max: 12, default: 0, errorMessage: "Time offset must be between -12 and +12 hours" }, 45 | }, 46 | "overlay.font": { 47 | optional: true, 48 | isString: { default: "swansea-bold.ttf", errorMessage: "Fonts must be in the fonts folder" }, 49 | }, 50 | "overlay.scrolling": { 51 | optional: true, 52 | isBoolean: { default: false, errorMessage: "Scrolling must be a boolean type" }, 53 | }, 54 | "overlay.image.file": { 55 | optional: true, 56 | isString: { default: "", errorMessage: "Image file must be a string" }, 57 | }, 58 | "overlay.image.size": { 59 | optional: true, 60 | isInt: { 61 | min: 0, 62 | max: 100, 63 | default: 50, 64 | errorMessage: "Size between 0 and 100, relative to view height", 65 | }, 66 | }, 67 | "overlay.image.location.x": { 68 | optional: true, 69 | isInt: { min: -100, max: 100, default: 0, errorMessage: "X-axis location between -100 and 100" }, 70 | }, 71 | "overlay.image.location.y": { 72 | optional: true, 73 | isInt: { min: -100, max: 100, default: 0, errorMessage: "Y-axis location between -100 and 100" }, 74 | }, 75 | }; 76 | }; 77 | 78 | // "topRight": { 79 | // "line1": "%{pts\\:hms}", 80 | // "line2": "Frame %{n}" 81 | // } 82 | -------------------------------------------------------------------------------- /validators/rtmp.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | const isIPorFQDN = require("@utils/validator-isiporfqdn"); 26 | 27 | module.exports = (direction = "input") => { 28 | return { 29 | [`${direction}.address`]: { 30 | exists: { errorMessage: "Must provide and address required." }, 31 | custom: { 32 | options: isIPorFQDN, 33 | errorMessage: "Address must be a valid IP Address or FQDN", 34 | }, 35 | }, 36 | [`${direction}.path`]: { 37 | optional: true, 38 | isString: { default: "", errorMessage: "Path must be a valid string" }, 39 | }, 40 | [`${direction}.port`]: { 41 | optional: true, 42 | exists: { errorMessage: "Must provide a port number as an integar" }, 43 | isInt: { 44 | min: 1024, 45 | max: 65535, 46 | default: 1935, 47 | errorMessage: "Must be a valid port number between 1024 to 65535", 48 | }, 49 | }, 50 | [`${direction}.key`]: { 51 | optional: true, 52 | isString: { 53 | default: "", 54 | errorMessage: "RTMP key must be a string.", 55 | }, 56 | }, 57 | [`${direction}.ttl`]: { 58 | optional: true, 59 | isInt: { 60 | min: 0, 61 | max: 255, 62 | default: 64, 63 | errorMessage: "Time to Live of SRT Packets must be between 0 and 255", 64 | }, 65 | }, 66 | [`${direction}.tos`]: { 67 | optional: true, 68 | isInt: { min: 0, max: 255, default: 104, errorMessage: "ToS of SRT Packets must be between 0 and 255" }, 69 | }, 70 | [`${direction}.bitrate`]: { 71 | optional: true, 72 | isString: { 73 | default: "5000k", 74 | errorMessage: "Bitrate must be a string.", 75 | }, 76 | }, 77 | [`${direction}.encodePreset`]: { 78 | optional: true, 79 | isIn: { 80 | options: [encodePresets], 81 | default: encodePresets[0], 82 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 83 | }, 84 | }, 85 | }; 86 | }; 87 | -------------------------------------------------------------------------------- /validators/rtp.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | const isIPorFQDN = require("@utils/validator-isiporfqdn"); 26 | 27 | module.exports = (direction = "input") => { 28 | return { 29 | [`${direction}.address`]: { 30 | exists: { errorMessage: "Must provide and address required." }, 31 | custom: { 32 | options: isIPorFQDN, 33 | errorMessage: "Address must be a valid IP Address or FQDN", 34 | }, 35 | }, 36 | [`${direction}.port`]: { 37 | exists: { errorMessage: "Must provide a port number as an integar" }, 38 | isInt: { min: 1024, max: 65535, errorMessage: "Must be a valid port number between 1024 to 65535" }, 39 | }, 40 | [`${direction}.packetSize`]: { 41 | optional: true, 42 | isInt: { 43 | min: 0, 44 | max: 65535, 45 | default: 1316, 46 | errorMessage: "RTP Packet size must be between 0 and 65535 bytes", 47 | }, 48 | }, 49 | [`${direction}.buffer`]: { 50 | optional: true, 51 | isInt: { min: 0, max: 255, default: 65535, errorMessage: "RTP buffer must be between 0 and 65535 bytes" }, 52 | }, 53 | [`${direction}.bitrate`]: { 54 | optional: true, 55 | isString: { 56 | default: "5000k", 57 | errorMessage: "Bitrate must be a string.", 58 | }, 59 | }, 60 | [`${direction}.encodePreset`]: { 61 | optional: true, 62 | isIn: { 63 | options: [encodePresets], 64 | default: encodePresets[0], 65 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 66 | }, 67 | }, 68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /validators/srt.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | const isIPorFQDN = require("@utils/validator-isiporfqdn"); 26 | 27 | module.exports = (direction = "input") => { 28 | return { 29 | [`${direction}.address`]: { 30 | exists: { errorMessage: "Must provide and address required." }, 31 | custom: { 32 | options: isIPorFQDN, 33 | errorMessage: "Address must be a valid IP Address or FQDN", 34 | }, 35 | }, 36 | [`${direction}.port`]: { 37 | exists: { errorMessage: "Must provide a port number as an integar" }, 38 | isInt: { min: 1024, max: 65535, errorMessage: "Must be a valid port number between 1024 to 65535" }, 39 | }, 40 | [`${direction}.latency`]: { 41 | optional: true, 42 | isInt: { min: 20, max: 10000, default: 250, errorMessage: "SRT Latency must be between 20ms and 10000ms" }, 43 | }, 44 | [`${direction}.packetSize`]: { 45 | optional: true, 46 | isInt: { 47 | min: 0, 48 | max: 65535, 49 | default: 1316, 50 | errorMessage: "SRT Packet size must be between 0 and 65535 bytes", 51 | }, 52 | }, 53 | [`${direction}.ttl`]: { 54 | optional: true, 55 | isInt: { 56 | min: 0, 57 | max: 255, 58 | default: 64, 59 | errorMessage: "Time to Live of SRT Packets must be between 0 and 255", 60 | }, 61 | }, 62 | [`${direction}.tos`]: { 63 | optional: true, 64 | isInt: { min: 0, max: 255, default: 104, errorMessage: "ToS of SRT Packets must be between 0 and 255" }, 65 | }, 66 | [`${direction}.mode`]: { 67 | optional: true, 68 | isIn: { 69 | options: [["listener", "caller"]], 70 | default: "caller", 71 | errorMessage: "SRT mode must be 'Caller' or 'Listener'", 72 | }, 73 | }, 74 | [`${direction}.bitrate`]: { 75 | optional: true, 76 | isString: { 77 | default: "5000k", 78 | errorMessage: "Bitrate must be a string.", 79 | }, 80 | }, 81 | [`${direction}.encodePreset`]: { 82 | optional: true, 83 | isIn: { 84 | options: [encodePresets], 85 | default: encodePresets[0], 86 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 87 | }, 88 | }, 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /validators/thumbnail.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (direction = "input") => { 25 | return { 26 | "thumbnail.frequency": { 27 | optional: true, 28 | isInt: { 29 | min: 1, 30 | max: 200, 31 | default: 25, 32 | errorMessage: "Thumbnail frequency must be between 1 and 200 frames.", 33 | }, 34 | }, 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /validators/udp.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | const encodePresets = require("@utils/encodePresets"); 25 | const isIPorFQDN = require("@utils/validator-isiporfqdn"); 26 | 27 | module.exports = (direction = "input") => { 28 | return { 29 | [`${direction}.address`]: { 30 | exists: { errorMessage: "Must provide and address required." }, 31 | custom: { 32 | options: isIPorFQDN, 33 | errorMessage: "Address must be a valid IP Address or FQDN", 34 | }, 35 | }, 36 | [`${direction}.port`]: { 37 | exists: { errorMessage: "Must provide a port number as an integar" }, 38 | isInt: { min: 1024, max: 65535, errorMessage: "Must be a valid port number between 1024 to 65535" }, 39 | }, 40 | [`${direction}.packetSize`]: { 41 | optional: true, 42 | isInt: { 43 | min: 0, 44 | max: 65535, 45 | default: 1316, 46 | errorMessage: "UDP packet size must be between 0 and 65535 bytes", 47 | }, 48 | }, 49 | [`${direction}.buffer`]: { 50 | optional: true, 51 | isInt: { min: 0, max: 255, default: 65535, errorMessage: "UDP buffer must be between 0 and 65535 bytes" }, 52 | }, 53 | [`${direction}.bitrate`]: { 54 | optional: true, 55 | isString: { 56 | default: "5000k", 57 | errorMessage: "Bitrate must be a string.", 58 | }, 59 | }, 60 | [`${direction}.encodePreset`]: { 61 | optional: true, 62 | isIn: { 63 | options: [encodePresets], 64 | default: encodePresets[0], 65 | errorMessage: `Encode preset must be one of ${encodePresets.toString()}.`, 66 | }, 67 | }, 68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /validators/vmaf.js: -------------------------------------------------------------------------------- 1 | /* 2 | FFmpeg Docker, an API wrapper around FFmpeg running in a configurable docker container 3 | Copyright (C) 2022 Ryan McCartney 4 | 5 | This file is part of the FFmpeg Docker (ffmpeg-docker). 6 | 7 | FFmpeg Docker is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | "use strict"; 23 | 24 | module.exports = (direction = "input") => { 25 | return { 26 | "vmaf.reference": { 27 | isString: { 28 | errorMessage: "VMAF reference file must be a string", 29 | }, 30 | }, 31 | "vmaf.model": { 32 | optional: true, 33 | isString: { 34 | default: "vmaf_v0.6.1.json", 35 | errorMessage: "VMAF model must be a string", 36 | }, 37 | }, 38 | "vmaf.threads": { 39 | optional: true, 40 | isInt: { min: 1, max: 20, default: 1, errorMessage: "VMAF threads must be a integar" }, 41 | }, 42 | }; 43 | }; 44 | -------------------------------------------------------------------------------- /views/chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | 6 | 7 | 8 | 9 | {{> header }} 10 | 11 |
12 | 13 |
14 | 15 | {{> footer }} 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /views/clock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | 6 | 42 | 43 | 44 | 45 | {{> header }} 46 | 47 |
48 |
49 |

50 |

51 |
52 |

FFmpeg Server Time

53 |

54 |
55 | 56 | {{> footer }} 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | 6 | 7 | 8 | {{> header }} 9 | 10 |
11 |

System Information

12 |
13 | 14 |
15 |
16 |
CPU Usage
17 | 18 |
19 |
20 |
21 |
22 |
Memory Usage
23 | 24 |
25 |
26 |
27 |
28 |
Disk Usage
29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 | 37 |
38 | 39 |

Media Upload

40 |
41 | 42 |
43 |
44 | 51 |
52 | 53 |
54 | 55 |
56 |
57 | 58 | 64 |
65 | {{> footer }} 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /views/jobs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | 13 | 14 | 15 | {{> header }} 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 |
#Job NameJob IDTypeStartedLoadProgressThumbnail 30 | 31 |
36 |
37 | 38 | {{> footer }} 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /views/partials/footer.html: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /views/partials/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | FFmpeg Docker | {{ title }} 9 | 10 | -------------------------------------------------------------------------------- /views/partials/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 68 | 69 | -------------------------------------------------------------------------------- /views/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | 6 | 7 | 8 | 9 | {{> header }} 10 | 11 |
12 | 18 |
19 | 20 | {{> footer }} 21 | 22 | 23 | 24 | 25 | --------------------------------------------------------------------------------