Sign inSign up

funnyzak/ffmpeg-service

By funnyzak

Updated 11 months ago

A lightweight, containerized FFmpeg media processing microservice built with Flask and Python.

Image
Web servers
0

4.5K

funnyzak/ffmpeg-service repository overview

FFmpeg Media Processing Service

Commit activity Docker Tags Image Size Docker Pulls

A lightweight, containerized FFmpeg media processing microservice built with Flask and Python. This service provides HTTP API endpoints for both video and audio processing, including metadata extraction, format conversion, and screenshot capture (for videos).

Build with the linux/arm64, linux/amd64, linux/arm/v7 architectures.

Features

  • Video Processing: Extract metadata, capture screenshots, convert formats, and adjust resolution
  • Audio Processing: Extract metadata and convert between audio formats
  • Format Conversion: Convert videos between popular formats (MP4, AVI, MOV, MKV, WebM) and audio between formats (MP3, WAV, FLAC, AAC, OGG, M4A, Opus)
  • Resolution Control: Convert videos to specific resolutions (720p, 1080p, 4K, custom dimensions)
  • Batch Processing: Combine multiple operations in a single API call
  • URL & File Upload Support: Process media from URLs or direct file uploads
  • Automatic Cleanup: Temporary files are automatically deleted after processing
  • Health Monitoring: Built-in health check endpoint
  • API Key Authentication: Optional API key authentication for secure access
  • Error Handling: Comprehensive error handling with appropriate HTTP status codes
  • Structured Logging: Comprehensive logging system with request tracing and performance monitoring
  • Flexible Configuration: All settings configurable via environment variables
  • Multi-Architecture Support: Supports ARM and x86 architectures

Pull

docker pull funnyzak/ffmpeg-service:latest
# GHCR
docker pull ghcr.io/funnyzak/ffmpeg-service:latest
# Aliyun
docker pull registry.cn-beijing.aliyuncs.com/funnyzak/ffmpeg-service:latest

Usage

Docker Run
# Run with default settings
docker run -d --name ffmpeg-service --restart on-failure \
  -p 8080:8080 \
  funnyzak/ffmpeg-service

# Run with custom configuration
docker run -d --name ffmpeg-service --restart on-failure \
  -p 8080:8080 \
  -e MAX_FILE_SIZE=1073741824 \
  -e FILE_RETENTION_HOURS=24 \
  -e CLEANUP_INTERVAL_MINUTES=60 \
  -e FLASK_DEBUG=false \
  -v ./temp:/tmp/videos \
  funnyzak/ffmpeg-service

# Run with environment file
docker run -d --name ffmpeg-service --restart on-failure \
  -p 8080:8080 \
  --env-file .env \
  -v ./temp:/tmp/videos \
  funnyzak/ffmpeg-service
Docker Compose

Create a docker-compose.yml file:

version: "3.8"
services:
  ffmpeg-service:
    image: funnyzak/ffmpeg-service:latest
    container_name: ffmpeg-service
    environment:
      # System settings
      - TZ=Asia/Shanghai
      
      # Application configuration
      - TEMP_DIR=/tmp/videos
      - MAX_FILE_SIZE=524288000      # 500MB in bytes
      - FILE_RETENTION_HOURS=2       # Keep output files for 2 hours
      - CLEANUP_INTERVAL_MINUTES=30  # Run cleanup every 30 minutes
      
      # Supported formats (comma-separated)
      - ALLOWED_VIDEO_EXTENSIONS=mp4,avi,mov,mkv,flv,wmv,webm,m4v
      - SUPPORTED_VIDEO_OUTPUT_FORMATS=mp4,avi,mov,mkv,webm
      - SUPPORTED_AUDIO_OUTPUT_FORMATS=mp3,wav,flac,aac,ogg,m4a,opus
      
      # Flask server settings
      - FLASK_HOST=0.0.0.0
      - FLASK_PORT=8080
      - FLASK_DEBUG=false
      
      # Gunicorn WSGI server settings
      - GUNICORN_WORKERS=4           # Number of worker processes
      - GUNICORN_WORKER_CLASS=sync   # Worker class (sync, gevent, eventlet)
      - GUNICORN_TIMEOUT=120         # Worker timeout in seconds
      - GUNICORN_MAX_REQUESTS=1000   # Restart workers after N requests
      - GUNICORN_MAX_REQUESTS_JITTER=100  # Add randomness to max requests
      
      # API Key authentication (optional)
      - API_KEYS=your_secret_key_here
      
      # Base URL for full path URLs (optional)
      # - BASE_URL=http://your-domain.com:8080
    ports:
      - "${HOST_PORT:-8080}:${FLASK_PORT:-8080}"
    restart: on-failure
    volumes:
      - ${TEMP_VOLUME:-./temp}:${TEMP_DIR:-/tmp/videos}
Using Environment File

Create a .env file for easier configuration management. You can use the provided env.example as a template:

# Copy the example file
cp env.example .env

# Edit the configuration
nano .env

Example .env file:

# Application configuration
MAX_FILE_SIZE=1073741824          # 1GB
FILE_RETENTION_HOURS=24           # Keep files for 24 hours
CLEANUP_INTERVAL_MINUTES=60       # Cleanup every hour

# Docker settings
HOST_PORT=8080
TEMP_VOLUME=./temp

# Flask settings
FLASK_DEBUG=false

# Gunicorn WSGI server settings
GUNICORN_WORKERS=4
GUNICORN_WORKER_CLASS=sync
GUNICORN_TIMEOUT=120
GUNICORN_MAX_REQUESTS=1000
GUNICORN_MAX_REQUESTS_JITTER=100

# API Key authentication (optional)
# Leave empty to disable authentication
# Multiple keys can be separated by commas: API_KEYS=key1,key2,key3
API_KEYS=your_secret_key_here

Then run:

docker-compose up -d
Docker Build
docker build \
  --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
  --build-arg VERSION="1.0.0" \
  -t funnyzak/ffmpeg-service:1.0.0 .
Local Development

For local development without Docker:

# Install system dependencies (Ubuntu/Debian)
sudo apt-get update && sudo apt-get install -y ffmpeg libmagic1

# Install Python dependencies
pip install -r requirements.txt

# Set environment variables (optional)
export MAX_FILE_SIZE=104857600
export FLASK_DEBUG=true
export FILE_RETENTION_HOURS=1

# Run the service
python app.py
Using the Startup Script

The project includes a startup script that automatically detects the environment:

# Make the script executable
chmod +x start.sh

# Run the service
./start.sh

The script will:

  • Use Gunicorn in Docker environments
  • Use Flask development server in local environments
  • Display configuration information on startup

Authentication

The service supports optional API key authentication to secure access to video processing endpoints.

Configuration

Set the API_KEYS environment variable to enable authentication:

# Single API key
API_KEYS=your_secret_key_here

# Multiple API keys (comma-separated)
API_KEYS=key1,key2,key3

# Disable authentication (default)
API_KEYS=
Protected Endpoints

The following endpoints require authentication when API_KEYS is configured:

  • POST /process - Media processing (video and audio)
  • POST /info - Media information extraction (video and audio)
Unprotected Endpoints

The following endpoints do not require authentication:

  • GET /health - Health check
  • GET /download/{filename} - File download
Usage

Include the API key in the X-API-Key header:

curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{"media_url": "https://example.com/video.mp4"}'
Error Responses
  • 401 Unauthorized: Missing API key
  • 403 Forbidden: Invalid API key

API Endpoints

Health Check
GET /health

Response:

{
  "code": 0,
  "msg": "Service is healthy",
  "data": {}
}
Media Processing (Combined Operations)
POST /process
Content-Type: application/json
X-API-Key: your_secret_key_here
Video Processing Example
Using URL
POST /process
Content-Type: application/json
X-API-Key: your_secret_key_here

Request Body:

{
  "media_url": "https://example.com/video.mp4",
  "extract_info": true,
  "take_screenshots": true,
  "screenshot_count": 5,
  "convert_format": "mp4",
  "convert_quality": "medium",
  "convert_resolution": "720p"
}
Using File Upload
POST /process
Content-Type: multipart/form-data
X-API-Key: your_secret_key_here

Request Body:

file: [video file]
extract_info: true
take_screenshots: true
screenshot_timestamps: [10, 30, 60]

Example with curl:

curl -X POST http://localhost:8080/process \
  -H "X-API-Key: your_secret_key_here" \
  -F "[email protected]" \
  -F "extract_info=true" \
  -F "take_screenshots=true" \
  -F "screenshot_count=5" \
  -F "convert_format=mp4" \
  -F "convert_resolution=720p"

Response (Video):

{
  "code": 0,
  "msg": "Media processing completed successfully",
  "data": {
    "media_type": "video",
    "info": {
      "duration": 120.5,
      "size": 15728640,
      "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
      "codec_name": "h264",
      "width": 1920,
      "height": 1080,
      "frame_rate": 30.0,
      "bit_rate": 1048576
    },
    "screenshots": [
      {
        "timestamp": 24.1,
        "filename": "screenshot_abc123_24.jpg",
        "file_size": 102400,
        "url": "/download/screenshot_abc123_24.jpg"
      }
    ],
    "conversion": {
      "filename": "converted_def456.mp4",
      "file_size": 12582912,
      "format": "mp4",
      "resolution": "720p",
      "url": "/download/converted_def456.mp4"
    }
  }
}

Note: When BASE_URL is configured, the url fields will contain full URLs instead of relative paths. For example: "url": "http://10.0.0.8:8080/download/converted_def456.mp4"


### Audio Processing Example

#### Using URL
```http
POST /process
Content-Type: application/json
X-API-Key: your_secret_key_here

Request Body:

{
  "media_url": "https://example.com/audio.wav",
  "extract_info": true,
  "convert_format": "mp3",
  "convert_quality": "high"
}
Using File Upload
POST /process
Content-Type: multipart/form-data
X-API-Key: your_secret_key_here

Request Body:

file: [audio file]
extract_info: true
convert_format: mp3
convert_quality: high

Example with curl:

curl -X POST http://localhost:8080/process \
  -H "X-API-Key: your_secret_key_here" \
  -F "[email protected]" \
  -F "extract_info=true" \
  -F "convert_format=mp3" \
  -F "convert_quality=high"

Response (Audio):

{
  "code": 0,
  "msg": "Media processing completed successfully",
  "data": {
    "media_type": "audio",
    "info": {
      "duration": 180.5,
      "size": 15728640,
      "format_name": "wav",
      "codec_name": "pcm_s16le",
      "sample_rate": 44100,
      "channels": 2,
      "bit_rate": 1411200,
      "channel_layout": "stereo"
    },
    "conversion": {
      "filename": "converted_audio_abc123.mp3",
      "file_size": 5242880,
      "format": "mp3",
      "url": "/download/converted_audio_abc123.mp3"
    }
  }
}
Media Information
POST /info
Content-Type: application/json
X-API-Key: your_secret_key_here

Video Request Body (URL):

{
  "media_url": "https://example.com/video.mp4"
}

Video Request Body (File Upload):

POST /info
Content-Type: multipart/form-data
X-API-Key: your_secret_key_here
file: [video file]

Example with curl:

curl -X POST http://localhost:8080/info \
  -H "X-API-Key: your_secret_key_here" \
  -F "[email protected]"

Video Response:

{
  "code": 0,
  "msg": "Media information extracted successfully",
  "data": {
    "media_type": "video",
    "info": {
      "duration": 120.5,
      "size": 15728640,
      "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
      "codec_name": "h264",
      "width": 1920,
      "height": 1080,
      "frame_rate": 30.0,
      "bit_rate": 1048576
    }
  }
}

Audio Request Body (URL):

{
  "media_url": "https://example.com/audio.mp3"
}

Audio Request Body (File Upload):

POST /info
Content-Type: multipart/form-data
X-API-Key: your_secret_key_here
file: [audio file]

Example with curl:

curl -X POST http://localhost:8080/info \
  -H "X-API-Key: your_secret_key_here" \
  -F "[email protected]"

Audio Response:

{
  "code": 0,
  "msg": "Media information extracted successfully",
  "data": {
    "media_type": "audio",
    "info": {
      "duration": 180.5,
      "size": 15728640,
      "format_name": "mp3",
      "codec_name": "mp3",
      "sample_rate": 44100,
      "channels": 2,
      "bit_rate": 320000,
      "channel_layout": "stereo"
    }
  }
}
Download Processed Files
GET /download/{filename}

Downloads the processed file (screenshot or converted video).

Request Parameters

Processing Options
ParameterTypeDescriptionDefault
media_urlstringURL to media file (video or audio)-
extract_infobooleanExtract media metadatatrue
take_screenshotsbooleanCapture screenshotsfalse
screenshot_timestampsarraySpecific timestamps for screenshots (seconds)-
screenshot_countintegerNumber of evenly spaced screenshots-
convert_formatstringTarget format (video: mp4, avi, mov, mkv, webm; audio: mp3, wav, flac, aac, ogg, m4a, opus)-
convert_qualitystringConversion quality (low, medium, high)medium
convert_resolutionstringTarget resolution (720p, 1080p, 1920x1080, etc.)original
File Upload

Alternatively, you can upload files directly using multipart/form-data:

Video File Upload Example
curl -X POST http://localhost:8080/process \
  -F "[email protected]" \
  -F "take_screenshots=true" \
  -F "screenshot_count=3" \
  -F "convert_format=mp4" \
  -F "convert_resolution=720p"
Audio File Upload Example
curl -X POST http://localhost:8080/process \
  -F "[email protected]" \
  -F "extract_info=true" \
  -F "convert_format=wav" \
  -F "convert_quality=high"

Supported Audio Formats for Upload:

  • MP3, WAV, FLAC, AAC, OGG, M4A, WMA, Opus
  • The service automatically detects audio files and processes them accordingly

Error Responses

The service returns standardized error responses:

{
  "code": 400,
  "msg": "Error description",
  "data": {}
}

Common Error Codes:

  • 400: Bad Request (invalid parameters, file too large, unsupported format)
  • 404: Not Found (file or endpoint not found)
  • 413: Payload Too Large (file exceeds 500MB limit)
  • 500: Internal Server Error (processing failed)

Configuration

All service configurations can be customized via environment variables. This makes it easy to adapt the service for different environments (development, testing, production) without code changes.

Gunicorn WSGI Server

The service uses Gunicorn as the WSGI server for production deployments, providing better performance and stability compared to Flask's development server.

Gunicorn Configuration Options
VariableDescriptionDefaultRecommended
GUNICORN_WORKERSNumber of worker processes4(2 x CPU cores) + 1
GUNICORN_WORKER_CLASSWorker class typesyncsync (for CPU-bound tasks)
GUNICORN_TIMEOUTWorker timeout in seconds120300 (for large files)
GUNICORN_MAX_REQUESTSRestart workers after N requests10001000-2000
GUNICORN_MAX_REQUESTS_JITTERAdd randomness to max requests100100-200
Worker Configuration Examples

CPU-Intensive Processing (Default):

GUNICORN_WORKERS=4
GUNICORN_WORKER_CLASS=sync
GUNICORN_TIMEOUT=120

High-Throughput Processing:

GUNICORN_WORKERS=8
GUNICORN_WORKER_CLASS=sync
GUNICORN_TIMEOUT=300
GUNICORN_MAX_REQUESTS=2000

Memory-Constrained Environment:

GUNICORN_WORKERS=2
GUNICORN_WORKER_CLASS=sync
GUNICORN_TIMEOUT=60
GUNICORN_MAX_REQUESTS=500
Performance Tuning
  1. Worker Count: Generally (2 x CPU cores) + 1 for CPU-bound tasks
  2. Worker Class: Use sync for FFmpeg processing (CPU-intensive)
  3. Timeout: Increase for large file processing (120-300 seconds)
  4. Max Requests: Restart workers periodically to prevent memory leaks
  5. Jitter: Add randomness to prevent all workers restarting simultaneously
Environment Variables
VariableDescriptionDefaultExample
TEMP_DIRTemporary files directory/tmp/videos/data/temp
MAX_FILE_SIZEMaximum file size in bytes524288000 (500MB)1073741824 (1GB)
FILE_RETENTION_HOURSHours to keep output files224
CLEANUP_INTERVAL_MINUTESCleanup task interval3060
ALLOWED_VIDEO_EXTENSIONSComma-separated input formatsmp4,avi,mov,mkv,flv,wmv,webm,m4vmp4,avi,mov
SUPPORTED_VIDEO_OUTPUT_FORMATSComma-separated video output formatsmp4,avi,mov,mkv,webmmp4,webm
SUPPORTED_AUDIO_OUTPUT_FORMATSComma-separated audio output formatsmp3,wav,flac,aac,ogg,m4a,opusmp3,wav
FLASK_HOSTFlask server host0.0.0.0127.0.0.1
FLASK_PORTFlask server port80809000
FLASK_DEBUGEnable debug modefalsetrue
GUNICORN_WORKERSNumber of Gunicorn worker processes48
GUNICORN_WORKER_CLASSGunicorn worker classsyncgevent
GUNICORN_TIMEOUTWorker timeout in seconds120300
GUNICORN_MAX_REQUESTSRestart workers after N requests10002000
GUNICORN_MAX_REQUESTS_JITTERAdd randomness to max requests100200
API_KEYSComma-separated API keys for authentication`` (disabled)key1,key2,key3
BASE_URLBase URL for full path URLs (optional)`` (relative URLs)http://10.0.0.8:8080
LOG_DIRDirectory where log files are stored./logs/tmp/logs
LOG_LEVELLog level: DEBUG, INFO, WARNING, ERROR, CRITICALINFODEBUG
Configuration Examples
Development Environment
FLASK_DEBUG=true
MAX_FILE_SIZE=104857600        # 100MB for testing
FILE_RETENTION_HOURS=1         # Quick cleanup
CLEANUP_INTERVAL_MINUTES=10    # Frequent cleanup
Production Environment
FLASK_DEBUG=false
MAX_FILE_SIZE=2147483648       # 2GB
FILE_RETENTION_HOURS=24        # Keep files longer
CLEANUP_INTERVAL_MINUTES=60    # Less frequent cleanup
ALLOWED_VIDEO_EXTENSIONS=mp4,mov,avi  # Limit formats
High-Security Environment
MAX_FILE_SIZE=52428800         # 50MB limit
FILE_RETENTION_HOURS=0.5       # 30 minutes retention
CLEANUP_INTERVAL_MINUTES=5     # Very frequent cleanup
SUPPORTED_VIDEO_OUTPUT_FORMATS=mp4   # Only MP4 output
SUPPORTED_AUDIO_OUTPUT_FORMATS=mp3   # Only MP3 output
Supported Formats

Video Input Formats:

  • MP4, AVI, MOV, MKV, FLV, WMV, WebM, M4V

Video Output Formats:

  • MP4, AVI, MOV, MKV, WebM

Audio Input Formats:

  • MP3, WAV, FLAC, AAC, OGG, M4A, WMA, Opus

Audio Output Formats:

  • MP3, WAV, FLAC, AAC, OGG, M4A, Opus
Quality Settings
  • Low: CRF 28 (smaller file size, lower quality)
  • Medium: CRF 23 (balanced)
  • High: CRF 18 (larger file size, higher quality)
Resolution Options

The service supports flexible resolution settings for video conversion:

Preset Resolutions
  • 240p: 426x240
  • 360p: 640x360
  • 480p: 854x480
  • 720p: 1280x720 (HD)
  • 1080p: 1920x1080 (Full HD)
  • 1440p: 2560x1440 (2K)
  • 2160p or 4k: 3840x2160 (4K Ultra HD)
Custom Resolutions
  • Width x Height: 1920x1080, 1280x720
  • Width : Height: 1920:1080, 1280:720
  • Single Dimension: 720 (height, preserves aspect ratio)
Examples
# Preset resolution
"convert_resolution": "720p"

# Custom resolution
"convert_resolution": "1920x1080"

# Preserve aspect ratio with specific height
"convert_resolution": "720"

# Keep original resolution (default)
"convert_resolution": null

Examples

Extract Video Information
curl -X POST http://localhost:8080/info \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{"media_url": "https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4"}'
Extract Audio Information
curl -X POST http://localhost:8080/info \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{"media_url": "https://example.com/audio.mp3"}'
Take 5 Screenshots
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{
    "media_url": "https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4",
    "take_screenshots": true,
    "screenshot_count": 5
  }'
Convert Video to MP4 with Resolution
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{
    "media_url": "https://sample-videos.com/zip/10/avi/SampleVideo_1280x720_1mb.avi",
    "convert_format": "mp4",
    "convert_quality": "high",
    "convert_resolution": "1080p"
  }'
Convert Audio to MP3
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{
    "media_url": "https://example.com/audio.wav",
    "convert_format": "mp3",
    "convert_quality": "high"
  }'
Combined Processing
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_secret_key_here" \
  -d '{
    "media_url": "https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4",
    "extract_info": true,
    "take_screenshots": true,
    "screenshot_timestamps": [5, 15, 25],
    "convert_format": "webm",
    "convert_quality": "medium",
    "convert_resolution": "720p"
  }'

Performance & Limitations

  • File Size Limit: Configurable via MAX_FILE_SIZE (default: 500MB)
  • Concurrent Processing: Limited by container resources and available memory
  • Temporary Storage: Files are automatically cleaned up based on FILE_RETENTION_HOURS
  • Timeout: 30 seconds for URL downloads
  • Memory Usage: Depends on video size and processing operations
  • Cleanup Frequency: Configurable via CLEANUP_INTERVAL_MINUTES
  • Supported Formats: Configurable via environment variables

Troubleshooting

Common Issues
  1. File Too Large Error

    • Reduce file size or increase MAX_FILE_SIZE environment variable
    • Example: docker run -e MAX_FILE_SIZE=1073741824 ... (1GB)
  2. Processing Timeout

    • Check video file integrity
    • Reduce processing complexity
    • Increase container resources
  3. Download Failed

    • Verify URL accessibility
    • Check network connectivity
    • Ensure URL points to a video file
  4. Unsupported Format

    • Add format to ALLOWED_VIDEO_EXTENSIONS, SUPPORTED_VIDEO_OUTPUT_FORMATS, or SUPPORTED_AUDIO_OUTPUT_FORMATS
    • Example: ALLOWED_VIDEO_EXTENSIONS=mp4,avi,mov,custom_format
    • Check FFmpeg codec support
  5. Files Not Cleaned Up

    • Check FILE_RETENTION_HOURS and CLEANUP_INTERVAL_MINUTES settings
    • Ensure sufficient disk space
    • Verify temp directory permissions
  6. Service Won't Start

    • Check environment variable syntax (no spaces in comma-separated values)
    • Verify port availability
    • Check container logs for detailed error messages
  7. Configuration Not Applied

    • Restart container after changing environment variables
    • Verify .env file format (no quotes around values)
    • Check environment variable names (case-sensitive)
  8. Invalid Resolution Format

    • Use supported formats: 720p, 1080p, 1920x1080, 1280:720
    • Check resolution limits (max: 7680x4320)
    • For aspect ratio preservation, use single dimension: 720
Logs

View container logs:

docker logs ffmpeg-service

Reference

Tag summary

Content type

Image

Digest

sha256:e1c28b24e

Size

239.9 MB

Last updated

11 months ago

docker pull funnyzak/ffmpeg-service