FFmpeg 多媒体处理教程 / Docker 部署
Docker 部署
概述
Docker 是一种轻量级的容器化技术,可以将 FFmpeg 及其依赖打包到容器中,实现一致的部署环境。本章介绍如何使用 Docker 部署 FFmpeg,包括硬件加速配置和流媒体服务搭建。
基础镜像
官方镜像
# 拉取官方镜像
docker pull jrottenberg/ffmpeg
# 查看镜像信息
docker run --rm jrottenberg/ffmpeg -version
# 运行基本命令
docker run --rm -v $(pwd):/work jrottenberg/ffmpeg -i /work/input.mp4 /work/output.mp4
常用 FFmpeg Docker 镜像
| 镜像 | 说明 | 大小 |
|---|---|---|
| jrottenberg/ffmpeg | 最流行的 FFmpeg 镜像 | ~70MB |
| linuxserver/ffmpeg | LinuxServer 维护 | ~80MB |
| mwader/static-ffmpeg | 静态编译版本 | ~30MB |
| alphine/ffmpeg | 基于 Alpine Linux | ~40MB |
自定义基础镜像
# Dockerfile.ubuntu
FROM ubuntu:22.04
# 安装依赖
RUN apt-get update && apt-get install -y \
software-properties-common \
&& add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 \
&& apt-get update \
&& apt-get install -y ffmpeg \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 构建镜像
docker build -t my-ffmpeg -f Dockerfile.ubuntu .
# 运行
docker run --rm -v $(pwd):/work my-ffmpeg -i input.mp4 output.mp4
Dockerfile 编写
基本 Dockerfile
# 基于 Alpine Linux(轻量级)
FROM alpine:3.18
# 安装 FFmpeg
RUN apk add --no-cache ffmpeg
# 设置工作目录
WORKDIR /work
# 设置入口点
ENTRYPOINT ["ffmpeg"]
# 默认参数
CMD ["-version"]
完整功能 Dockerfile
# 基于 Ubuntu
FROM ubuntu:22.04
# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive
# 安装 FFmpeg 和依赖
RUN apt-get update && apt-get install -y \
ffmpeg \
libavcodec-extra \
frei0r-plugins \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# 创建工作目录
WORKDIR /work
# 创建输出目录
RUN mkdir -p /output
# 设置入口点
ENTRYPOINT ["ffmpeg"]
# 默认参数
CMD ["-version"]
带编解码器支持的 Dockerfile
# 支持更多编解码器
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装依赖
RUN apt-get update && apt-get install -y \
software-properties-common \
&& add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 \
&& apt-get update \
&& apt-get install -y \
ffmpeg \
libx264-dev \
libx265-dev \
libvpx-dev \
libfdk-aac-dev \
libmp3lame-dev \
libopus-dev \
libass-dev \
libfreetype6-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
从源码编译 Dockerfile
# 从源码编译 FFmpeg
FROM ubuntu:22.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# 安装编译依赖
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
nasm \
yasm \
pkg-config \
libx264-dev \
libx265-dev \
libvpx-dev \
libfdk-aac-dev \
libmp3lame-dev \
libopus-dev \
libass-dev \
libfreetype6-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# 下载 FFmpeg 源码
RUN git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg
WORKDIR /ffmpeg
# 编译 FFmpeg
RUN ./configure \
--prefix=/usr/local \
--enable-gpl \
--enable-nonfree \
--enable-libx264 \
--enable-libx265 \
--enable-libvpx \
--enable-libfdk-aac \
--enable-libmp3lame \
--enable-libopus \
--enable-libass \
--enable-libfreetype \
&& make -j$(nproc) \
&& make install
# 最终镜像
FROM ubuntu:22.04
# 复制编译好的 FFmpeg
COPY --from=builder /usr/local /usr/local
# 安装运行时依赖
RUN apt-get update && apt-get install -y \
libx264-163 \
libx265-199 \
libvpx7 \
libfdk-aac2 \
libmp3lame0 \
libopus0 \
libass9 \
libfreetype6 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
硬件加速配置
NVIDIA GPU 支持
# 支持 NVIDIA GPU
FROM nvidia/cuda:12.0.0-runtime-ubuntu22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装 FFmpeg
RUN apt-get update && apt-get install -y \
ffmpeg \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 GPU 的容器
docker run --rm --gpus all \
-v $(pwd):/work \
nvidia-ffmpeg \
-hwaccel cuda -i input.mp4 \
-c:v h264_nvenc output.mp4
NVIDIA Docker Compose
# docker-compose.yml
version: '3.8'
services:
ffmpeg:
build:
context: .
dockerfile: Dockerfile.nvidia
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
volumes:
- ./input:/work/input
- ./output:/work/output
command: >
-hwaccel cuda
-i /work/input/input.mp4
-c:v h264_nvenc
-preset fast
/work/output/output.mp4
Intel QSV 支持
# 支持 Intel QSV
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装 Intel Media SDK 和 FFmpeg
RUN apt-get update && apt-get install -y \
ffmpeg \
intel-media-va-driver \
i965-va-driver \
vainfo \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 QSV 的容器
docker run --rm \
--device /dev/dri:/dev/dri \
-v $(pwd):/work \
qsv-ffmpeg \
-hwaccel qsv -i input.mp4 \
-c:v h264_qsv output.mp4
VAAPI 支持(AMD/Intel)
# 支持 VAAPI
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装 VAAPI 和 FFmpeg
RUN apt-get update && apt-get install -y \
ffmpeg \
mesa-va-drivers \
vainfo \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 VAAPI 的容器
docker run --rm \
--device /dev/dri:/dev/dri \
-v $(pwd):/work \
vaapi-ffmpeg \
-hwaccel vaapi -hwaccel_device /dev/dri/renderD128 \
-i input.mp4 \
-c:v h264_vaapi output.mp4
Docker Compose
基本 Compose 配置
# docker-compose.yml
version: '3.8'
services:
ffmpeg:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./input:/work/input
- ./output:/work/output
working_dir: /work
# 默认命令
command: ["-version"]
批量处理 Compose
# docker-compose.batch.yml
version: '3.8'
services:
# 批量转码服务
transcode:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./input:/work/input
- ./output:/work/output
working_dir: /work
entrypoint: ["/bin/sh", "-c"]
command:
- |
for file in input/*.mp4; do
filename=$$(basename "$$file")
echo "处理: $$filename"
ffmpeg -y -i "$$file" \
-c:v libx264 -crf 23 -preset medium \
-c:a aac -b:a 128k \
"output/$$filename"
done
多服务 Compose
# docker-compose.services.yml
version: '3.8'
services:
# 转码服务
transcoder:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./videos:/videos
networks:
- ffmpeg-net
deploy:
replicas: 3
# HLS 生成服务
hls-generator:
build:
context: .
dockerfile: Dockerfile.hls
volumes:
- ./videos:/videos
- ./hls:/hls
networks:
- ffmpeg-net
# Web 服务器
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./hls:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- ffmpeg-net
networks:
ffmpeg-net:
流媒体服务
Nginx RTMP 服务
# Dockerfile.nginx-rtmp
FROM tiangolo/nginx-rtmp
# 复制配置
COPY nginx.conf /etc/nginx/nginx.conf
# 创建目录
RUN mkdir -p /var/www/html /var/log/nginx
EXPOSE 1935 80
# nginx.conf
worker_processes auto;
rtmp_auto_push on;
events {
worker_connections 1024;
}
rtmp {
server {
listen 1935;
application live {
live on;
record off;
# HLS
hls on;
hls_path /var/www/html/live;
hls_fragment 3;
hls_playlist_length 60;
# 录制
# record all;
# record_path /var/www/html/recordings;
# record_unique on;
}
}
}
http {
server {
listen 80;
location /live {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www/html;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}
}
}
# docker-compose.rtmp.yml
version: '3.8'
services:
nginx-rtmp:
build:
context: .
dockerfile: Dockerfile.nginx-rtmp
ports:
- "1935:1935"
- "8080:80"
volumes:
- ./hls:/var/www/html
restart: unless-stopped
ffmpeg-transcoder:
image: jrottenberg/ffmpeg
depends_on:
- nginx-rtmp
volumes:
- ./videos:/videos
command: >
-re -i /videos/input.mp4
-c:v libx264 -preset fast -tune zerolatency
-b:v 3M -maxrate 3M -bufsize 6M
-c:a aac -b:a 128k
-f flv rtmp://nginx-rtmp/live/stream
restart: unless-stopped
SRS(Simple Realtime Server)
# docker-compose.srs.yml
version: '3.8'
services:
srs:
image: ossrs/srs:5
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
volumes:
- ./conf:/usr/local/srs/conf
- ./objs/nginx/html:/usr/local/srs/objs/nginx/html
restart: unless-stopped
MediaMTX(原 rtsp-simple-server)
# docker-compose.mediamtx.yml
version: '3.8'
services:
mediamtx:
image: bluenviron/mediamtx
ports:
- "8554:8554" # RTSP
- "1935:1935" # RTMP
- "8888:8888" # HLS
- "8889:8889" # WebRTC
volumes:
- ./mediamtx.yml:/mediamtx.yml
restart: unless-stopped
实用 Docker 配置
持久化存储
# docker-compose.storage.yml
version: '3.8'
services:
ffmpeg:
image: jrottenberg/ffmpeg
volumes:
# 本地目录
- ./input:/work/input
- ./output:/work/output
# Docker 命名卷
- videos:/videos
# 只读挂载
- ./config:/config:ro
volumes:
videos:
driver: local
环境变量配置
# docker-compose.env.yml
version: '3.8'
services:
ffmpeg:
build:
context: .
dockerfile: Dockerfile
environment:
- FFMPEG_CODEC=libx264
- FFMPEG_CRF=23
- FFMPEG_PRESET=medium
- FFMPEG_AUDIO_CODEC=aac
- FFMPEG_AUDIO_BITRATE=128k
volumes:
- ./input:/work/input
- ./output:/work/output
entrypoint: ["/bin/sh", "-c"]
command:
- |
for file in /work/input/*.mp4; do
filename=$$(basename "$$file")
ffmpeg -y -i "$$file" \
-c:v $${FFMPEG_CODEC} -crf $${FFMPEG_CRF} -preset $${FFMPEG_PRESET} \
-c:a $${FFMPEG_AUDIO_CODEC} -b:a $${FFMPEG_AUDIO_BITRATE} \
"/work/output/$$filename"
done
资源限制
# docker-compose.resources.yml
version: '3.8'
services:
ffmpeg:
image: jrottenberg/ffmpeg
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
volumes:
- ./input:/work/input
- ./output:/work/output
健康检查
# docker-compose.health.yml
version: '3.8'
services:
ffmpeg:
build:
context: .
dockerfile: Dockerfile
healthcheck:
test: ["CMD", "ffmpeg", "-version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
volumes:
- ./input:/work/input
- ./output:/work/output
高级用法
多阶段构建
# Dockerfile.multistage
# 阶段 1: 编译
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y \
build-essential \
git \
nasm \
yasm \
libx264-dev \
libx265-dev \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg
WORKDIR /ffmpeg
RUN ./configure \
--prefix=/usr/local \
--enable-gpl \
--enable-libx264 \
--enable-libx265 \
&& make -j$(nproc) \
&& make install
# 阶段 2: 运行时
FROM ubuntu:22.04
COPY --from=builder /usr/local /usr/local
RUN apt-get update && apt-get install -y \
libx264-163 \
libx265-199 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
脚本封装
# Dockerfile.script
FROM jrottenberg/ffmpeg
# 复制处理脚本
COPY scripts/ /scripts/
RUN chmod +x /scripts/*.sh
WORKDIR /work
ENTRYPOINT ["/bin/sh"]
#!/bin/bash
# scripts/batch_transcode.sh
INPUT_DIR=${1:-/work/input}
OUTPUT_DIR=${2:-/work/output}
CODEC=${3:-libx264}
CRF=${4:-23}
mkdir -p "$OUTPUT_DIR"
for file in "$INPUT_DIR"/*.mp4; do
filename=$(basename "$file")
echo "处理: $filename"
ffmpeg -y -i "$file" \
-c:v "$CODEC" -crf "$CRF" \
-c:a aac -b:a 128k \
"$OUTPUT_DIR/$filename"
done
# docker-compose.script.yml
version: '3.8'
services:
batch-transcode:
build:
context: .
dockerfile: Dockerfile.script
volumes:
- ./input:/work/input
- ./output:/work/output
command: ["/scripts/batch_transcode.sh", "/work/input", "/work/output", "libx264", "23"]
Web API 服务
# app.py
from flask import Flask, request, jsonify, send_file
import subprocess
import os
import uuid
app = Flask(__name__)
UPLOAD_DIR = '/work/uploads'
OUTPUT_DIR = '/work/outputs'
os.makedirs(UPLOAD_DIR, exist_ok=True)
os.makedirs(OUTPUT_DIR, exist_ok=True)
@app.route('/transcode', methods=['POST'])
def transcode():
if 'file' not in request.files:
return jsonify({'error': 'No file provided'}), 400
file = request.files['file']
filename = f"{uuid.uuid4()}.mp4"
input_path = os.path.join(UPLOAD_DIR, filename)
output_path = os.path.join(OUTPUT_DIR, f"output_{filename}")
file.save(input_path)
# 获取参数
crf = request.form.get('crf', '23')
preset = request.form.get('preset', 'medium')
# 执行转码
cmd = [
'ffmpeg', '-y', '-i', input_path,
'-c:v', 'libx264', '-crf', crf, '-preset', preset,
'-c:a', 'aac', '-b:a', '128k',
output_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
return jsonify({
'success': True,
'output': f"/download/{os.path.basename(output_path)}"
})
else:
return jsonify({'error': result.stderr}), 500
@app.route('/download/<filename>')
def download(filename):
return send_file(
os.path.join(OUTPUT_DIR, filename),
as_attachment=True
)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# Dockerfile.api
FROM jrottenberg/ffmpeg
# 安装 Python 和 Flask
RUN apk add --no-cache python3 py3-pip
RUN pip3 install flask
# 复制应用
COPY app.py /app/app.py
WORKDIR /app
EXPOSE 5000
CMD ["python3", "app.py"]
# docker-compose.api.yml
version: '3.8'
services:
ffmpeg-api:
build:
context: .
dockerfile: Dockerfile.api
ports:
- "5000:5000"
volumes:
- uploads:/work/uploads
- outputs:/work/outputs
restart: unless-stopped
volumes:
uploads:
outputs:
最佳实践
镜像优化
# 优化的 Dockerfile
FROM alpine:3.18 AS builder
# 安装编译依赖
RUN apk add --no-cache \
build-base \
nasm \
yasm \
x264-dev \
x265-dev \
libvpx-dev
# 编译 FFmpeg
RUN wget https://ffmpeg.org/releases/ffmpeg-6.0.tar.gz \
&& tar xzf ffmpeg-6.0.tar.gz \
&& cd ffmpeg-6.0 \
&& ./configure \
--prefix=/usr/local \
--enable-gpl \
--enable-libx264 \
--enable-libx265 \
--enable-libvpx \
--disable-doc \
--disable-debug \
&& make -j$(nproc) \
&& make install
# 最终镜像
FROM alpine:3.18
COPY --from=builder /usr/local /usr/local
RUN apk add --no-cache \
x264-libs \
x265-libs \
libvpx
# 清理
RUN rm -rf /var/cache/apk/*
WORKDIR /work
ENTRYPOINT ["ffmpeg"]
安全配置
# docker-compose.security.yml
version: '3.8'
services:
ffmpeg:
image: jrottenberg/ffmpeg
# 非 root 用户运行
user: "1000:1000"
# 只读文件系统
read_only: true
# 临时文件系统
tmpfs:
- /tmp
# 安全选项
security_opt:
- no-new-privileges:true
# 资源限制
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
volumes:
- ./input:/work/input:ro
- ./output:/work/output
日志配置
# docker-compose.logging.yml
version: '3.8'
services:
ffmpeg:
image: jrottenberg/ffmpeg
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
- ./input:/work/input
- ./output:/work/output
常见问题
问题 1:权限问题
# 解决权限问题
docker run --rm -v $(pwd):/work --user $(id -u):$(id -g) jrottenberg/ffmpeg -i input.mp4 output.mp4
问题 2:GPU 访问问题
# 检查 GPU 是否可用
docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi
# 确保安装 nvidia-container-toolkit
# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html
问题 3:网络问题
# 使用 host 网络
services:
ffmpeg:
image: jrottenberg/ffmpeg
network_mode: host
注意事项
- 镜像大小:选择合适的 base 镜像,避免过大
- 权限管理:使用非 root 用户运行容器
- 资源限制:设置 CPU 和内存限制
- 存储管理:使用 volumes 持久化数据
- 网络安全:限制容器网络访问
业务场景
场景 1:视频转码微服务
# docker-compose.transcoding.yml
version: '3.8'
services:
# Redis 队列
redis:
image: redis:alpine
ports:
- "6379:6379"
# 转码 Worker
worker:
build:
context: .
dockerfile: Dockerfile.worker
depends_on:
- redis
environment:
- REDIS_URL=redis://redis:6379
deploy:
replicas: 4
volumes:
- videos:/videos
# API 服务
api:
build:
context: .
dockerfile: Dockerfile.api
ports:
- "5000:5000"
depends_on:
- redis
environment:
- REDIS_URL=redis://redis:6379
volumes:
- videos:/videos
volumes:
videos:
场景 2:直播转码集群
# docker-compose.live.yml
version: '3.8'
services:
# Nginx RTMP
nginx-rtmp:
image: tiangolo/nginx-rtmp
ports:
- "1935:1935"
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- hls:/var/www/html
# 转码服务
transcoder:
image: jrottenberg/ffmpeg
depends_on:
- nginx-rtmp
command: >
-re -i rtmp://nginx-rtmp/live/source
-c:v libx264 -preset fast -tune zerolatency
-b:v 3M -maxrate 3M -bufsize 6M
-c:a aac -b:a 128k
-f flv rtmp://nginx-rtmp/live/stream
deploy:
replicas: 2
restart: unless-stopped
# HLS 服务器
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- hls:/usr/share/nginx/html
- ./nginx-hls.conf:/etc/nginx/nginx.conf:ro
volumes:
hls:
扩展阅读
总结
本章介绍了 FFmpeg 的 Docker 部署,包括:
- Docker 镜像选择和构建
- 硬件加速配置
- Docker Compose 编排
- 流媒体服务搭建
掌握 Docker 部署可以帮助您构建可移植、可扩展的视频处理系统。