A modern Ruby wrapper for FFmpeg with zero dependencies. Provides clean APIs for metadata extraction, transcoding, scene detection, and keyframe extraction.
- 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
Add to your Gemfile:
gem'ruby-ffmpeg'Or install directly:
gem install ruby-ffmpegNote: FFmpeg must be installed on your system. Install with:
- macOS:
brew install ffmpeg - Ubuntu:
sudo apt install ffmpeg - Windows:
choco install ffmpeg
require'ffmpeg'# Load a videomedia=FFMPEG::Media.new("/path/to/video.mp4")# Get metadatamedia.duration# => 120.5media.resolution# => "1920x1080"media.video_codec# => "h264"media.audio_codec# => "aac"media.frame_rate# => 29.97media.bit_rate# => 5000000# Check propertiesmedia.valid?# => truemedia.video?# => truemedia.audio?# => truemedia.hd?# => truemedia.portrait?# => false# Basic transcodingmedia.transcode("/output.mp4")# With optionsmedia.transcode("/output.webm",video_codec: "libvpx-vp9",audio_codec: "libopus",resolution: "1280x720",video_bitrate: "2M")# With progress callbackmedia.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 videomedia.transcode("/clip.mp4",seek: 30,# Start at 30 secondsduration: 10# 10 second clip)Detect scene changes for video analysis, chapter generation, or highlight extraction:
# Detect scenes with default thresholdscenes=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 optionsdetector=FFMPEG::SceneDetector.new(media)scenes=detector.detect(threshold: 0.3,min_scene_length: 2.0,# Minimum 2 seconds between scenesmax_scenes: 20# Maximum 20 scenes)# Detect scenes and extract keyframesscenes=detector.detect_with_keyframes(threshold: 0.3,output_dir: "/tmp/scenes")# Each scene now has :keyframe_pathExtract frames for thumbnails, video analysis, or sprite sheets:
# Extract at regular intervalsframes=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 framesframes=media.extract_keyframes(output_dir: "/tmp/frames",count: 10# 10 evenly-distributed frames)# Extract at specific timestampsframes=media.extract_keyframes(output_dir: "/tmp/frames",timestamps: [0,30,60,90,120])# Using KeyframeExtractor directly for more optionsextractor=FFMPEG::KeyframeExtractor.new(media)# Extract actual I-frames (keyframes) from videoiframes=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, ... }Access detailed stream information:
# Video streamvideo=media.videovideo.codec# => "h264"video.width# => 1920video.height# => 1080video.frame_rate# => 29.97video.bit_rate# => 4500000video.pixel_format# => "yuv420p"video.rotation# => 0# Audio streamaudio=media.audioaudio.codec# => "aac"audio.sample_rate# => 48000audio.channels# => 2audio.channel_layout# => "stereo"# All streamsmedia.streams.eachdo |stream|
putsstream.to_sendFFMPEG.configuredo |config|
# Custom binary pathsconfig.ffmpeg_binary="/usr/local/bin/ffmpeg"config.ffprobe_binary="/usr/local/bin/ffprobe"# Default timeout (seconds)config.timeout=600# Enable loggingconfig.logger=Logger.new(STDOUT)# Default codecsconfig.default_video_codec="libx264"config.default_audio_codec="aac"# Number of threads (0 = auto)config.threads=0# Temporary directoryconfig.temp_dir="/tmp/ffmpeg"# Overwrite existing filesconfig.overwrite_output=trueendAutomatically extract video metadata for Active Storage:
# config/initializers/active_storage.rbRails.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", ... }Generate video previews:
# config/initializers/active_storage.rbRails.application.config.active_storage.previewers.prepend(FFMPEG::ActiveStorage::Previewer)# 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]beginmedia=FFMPEG::Media.new("/path/to/video.mp4")media.transcode("/output.mp4")rescueFFMPEG::MediaNotFound=>eputs"File not found: #{e.message}"rescueFFMPEG::InvalidMedia=>eputs"Invalid media: #{e.message}"rescueFFMPEG::TranscodingError=>eputs"Transcoding failed: #{e.message}"puts"Command: #{e.command}"puts"Output: #{e.output}"rescueFFMPEG::CommandTimeout=>eputs"Command timed out: #{e.message}"end| 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 |
# 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- Fork it
- Create your feature branch (
git checkout -b feature/my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin feature/my-new-feature) - Create a new Pull Request
The gem is available as open source under the terms of the MIT License.
Developed and maintained by Active Agents.
Inspired by streamio-ffmpeg and instructure/ruby-ffmpeg.