0.0
No release in over 3 years
A clean, well-tested FFmpeg wrapper with modern Ruby conventions, proper error handling, and zero dependencies. Features include metadata extraction, transcoding with progress reporting, scene detection, keyframe extraction, and Rails/Active Storage integration. Tested against FFmpeg 4, 5, 6, and 7.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 5.0
~> 13.0
~> 1.21
~> 0.9
 Project Readme

ruby-ffmpeg

A modern Ruby wrapper for FFmpeg with zero dependencies. Provides clean APIs for metadata extraction, transcoding, scene detection, and keyframe extraction.

Gem Version Ruby

Features

  • Zero Dependencies - Pure Ruby, no gem dependencies
  • Modern Ruby - Requires Ruby 3.1+, uses Data classes and modern patterns
  • FFmpeg 4-7 Support - Tested against FFmpeg versions 4, 5, 6, and 7
  • Scene Detection - Detect scene changes for video analysis
  • Keyframe Extraction - Extract frames at intervals, timestamps, or I-frames
  • Progress Reporting - Real-time transcoding progress callbacks
  • Rails Integration - Active Storage analyzer and previewer included

Installation

Add to your Gemfile:

gem 'ruby-ffmpeg'

Or install directly:

gem install ruby-ffmpeg

Note: FFmpeg must be installed on your system. Install with:

  • macOS: brew install ffmpeg
  • Ubuntu: sudo apt install ffmpeg
  • Windows: choco install ffmpeg

Quick Start

require 'ffmpeg'

# Load a video
media = FFMPEG::Media.new("/path/to/video.mp4")

# Get metadata
media.duration      # => 120.5
media.resolution    # => "1920x1080"
media.video_codec   # => "h264"
media.audio_codec   # => "aac"
media.frame_rate    # => 29.97
media.bit_rate      # => 5000000

# Check properties
media.valid?        # => true
media.video?        # => true
media.audio?        # => true
media.hd?           # => true
media.portrait?     # => false

Transcoding

# Basic transcoding
media.transcode("/output.mp4")

# With options
media.transcode("/output.webm",
  video_codec: "libvpx-vp9",
  audio_codec: "libopus",
  resolution: "1280x720",
  video_bitrate: "2M"
)

# With progress callback
media.transcode("/output.mp4") do |progress|
  puts "Progress: #{progress.round(1)}%"
end

# Copy streams (no re-encoding)
media.transcode("/output.mp4",
  copy_video: true,
  copy_audio: true
)

# Trim video
media.transcode("/clip.mp4",
  seek: 30,      # Start at 30 seconds
  duration: 10   # 10 second clip
)

Scene Detection

Detect scene changes for video analysis, chapter generation, or highlight extraction:

# Detect scenes with default threshold
scenes = media.detect_scenes
# => [
#   { timestamp: 0.0, score: 1.0 },
#   { timestamp: 5.23, score: 0.45 },
#   { timestamp: 12.8, score: 0.38 }
# ]

# Custom threshold (lower = more sensitive)
scenes = media.detect_scenes(threshold: 0.2)

# Using SceneDetector directly for more options
detector = FFMPEG::SceneDetector.new(media)
scenes = detector.detect(
  threshold: 0.3,
  min_scene_length: 2.0,  # Minimum 2 seconds between scenes
  max_scenes: 20          # Maximum 20 scenes
)

# Detect scenes and extract keyframes
scenes = detector.detect_with_keyframes(
  threshold: 0.3,
  output_dir: "/tmp/scenes"
)
# Each scene now has :keyframe_path

Keyframe Extraction

Extract frames for thumbnails, video analysis, or sprite sheets:

# Extract at regular intervals
frames = media.extract_keyframes(
  output_dir: "/tmp/frames",
  interval: 5.0  # Every 5 seconds
)
# => ["/tmp/frames/frame_0000.jpg", "/tmp/frames/frame_0005.jpg", ...]

# Extract specific number of frames
frames = media.extract_keyframes(
  output_dir: "/tmp/frames",
  count: 10  # 10 evenly-distributed frames
)

# Extract at specific timestamps
frames = media.extract_keyframes(
  output_dir: "/tmp/frames",
  timestamps: [0, 30, 60, 90, 120]
)

# Using KeyframeExtractor directly for more options
extractor = FFMPEG::KeyframeExtractor.new(media)

# Extract actual I-frames (keyframes) from video
iframes = extractor.extract_iframes(
  output_dir: "/tmp/iframes",
  max_frames: 50
)

# Create a thumbnail sprite sheet (for video scrubbing)
sprite = extractor.create_sprite(
  columns: 10,
  rows: 10,
  width: 160,
  output_path: "/tmp/sprite.jpg"
)
# => { path: "/tmp/sprite.jpg", columns: 10, rows: 10, ... }

Stream Information

Access detailed stream information:

# Video stream
video = media.video
video.codec         # => "h264"
video.width         # => 1920
video.height        # => 1080
video.frame_rate    # => 29.97
video.bit_rate      # => 4500000
video.pixel_format  # => "yuv420p"
video.rotation      # => 0

# Audio stream
audio = media.audio
audio.codec         # => "aac"
audio.sample_rate   # => 48000
audio.channels      # => 2
audio.channel_layout # => "stereo"

# All streams
media.streams.each do |stream|
  puts stream.to_s
end

Configuration

FFMPEG.configure do |config|
  # Custom binary paths
  config.ffmpeg_binary = "/usr/local/bin/ffmpeg"
  config.ffprobe_binary = "/usr/local/bin/ffprobe"

  # Default timeout (seconds)
  config.timeout = 600

  # Enable logging
  config.logger = Logger.new(STDOUT)

  # Default codecs
  config.default_video_codec = "libx264"
  config.default_audio_codec = "aac"

  # Number of threads (0 = auto)
  config.threads = 0

  # Temporary directory
  config.temp_dir = "/tmp/ffmpeg"

  # Overwrite existing files
  config.overwrite_output = true
end

Rails Integration

Active Storage Analyzer

Automatically extract video metadata for Active Storage:

# config/initializers/active_storage.rb
Rails.application.config.active_storage.analyzers.prepend(
  FFMPEG::ActiveStorage::Analyzer
)

Now video blobs will have metadata like:

video.metadata
# => { width: 1920, height: 1080, duration: 120.5, video_codec: "h264", ... }

Active Storage Previewer

Generate video previews:

# config/initializers/active_storage.rb
Rails.application.config.active_storage.previewers.prepend(
  FFMPEG::ActiveStorage::Previewer
)

Rake Tasks

# Check FFmpeg installation
rake ffmpeg:check

# Analyze a video
rake ffmpeg:analyze[/path/to/video.mp4]

# Extract keyframes
rake ffmpeg:keyframes[/path/to/video.mp4,output_dir,5]

# Detect scenes
rake ffmpeg:scenes[/path/to/video.mp4,0.3]

Error Handling

begin
  media = FFMPEG::Media.new("/path/to/video.mp4")
  media.transcode("/output.mp4")
rescue FFMPEG::MediaNotFound => e
  puts "File not found: #{e.message}"
rescue FFMPEG::InvalidMedia => e
  puts "Invalid media: #{e.message}"
rescue FFMPEG::TranscodingError => e
  puts "Transcoding failed: #{e.message}"
  puts "Command: #{e.command}"
  puts "Output: #{e.output}"
rescue FFMPEG::CommandTimeout => e
  puts "Command timed out: #{e.message}"
end

Comparison with streamio-ffmpeg

Feature ruby-ffmpeg streamio-ffmpeg
Ruby Version 3.1+ 2.0+
FFmpeg Version 4-7 2.8.4
Dependencies None None
Scene Detection Built-in No
Keyframe Extraction Built-in No
Active Storage Built-in No
Progress Callbacks Yes Yes
Last Updated 2026 2016

Development

# Clone the repo
git clone https://github.com/activeagents/ruby-ffmpeg.git
cd ruby-ffmpeg

# Install dependencies
bin/setup

# Run tests
rake test

# Run linter
rake rubocop

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create a new Pull Request

License

The gem is available as open source under the terms of the MIT License.

Credits

Developed and maintained by Active Agents.

Inspired by streamio-ffmpeg and instructure/ruby-ffmpeg.