Streaming made easy: convert videos to HLS and MPEG-DASH with Ruby

Delivering video content efficiently requires adaptive streaming formats like HLS (HTTP Live Streaming) and MPEG-DASH. In this guide, we demonstrate how to convert videos to these formats using Ruby, FFmpeg, and the open-source Streamio FFMPEG gem. By automating video conversion tasks, you can enhance streaming quality across diverse devices and network conditions.
Introduction to HLS and MPEG-DASH
HLS is a streaming protocol developed by Apple that segments video content into small, HTTP-based files, enabling adaptive bitrate streaming. MPEG-DASH (Dynamic Adaptive Streaming over HTTP) is a similar standard that adjusts video quality dynamically based on the user's network conditions.
Implementing these formats improves the viewer's experience by reducing buffering and optimizing video quality. Ensuring smooth streaming across various devices and networks is essential for modern applications.
Setting up your Ruby environment for video conversion
Start by confirming your Ruby version. Although Ruby 2.0 works, we recommend using Ruby 3.2.3 or later for improved performance and security.
Below is the compatibility matrix for the required components:
Component | Minimum Version | Recommended Version |
---|---|---|
Ruby | 2.0 | 3.2.3+ |
FFmpeg | 2.8.4 | 6.1.1+ |
streamio-ffmpeg | 3.0.2 | 3.0.2 |
Verify your Ruby installation:
ruby -v
If you need to install or upgrade Ruby, use a version manager like RVM or rbenv.
Next, install FFmpeg, which is essential for processing video files.
For macOS users:
brew install ffmpeg
For Ubuntu/Debian users:
sudo apt-get update
sudo apt-get install ffmpeg
Create a new project directory and initialize it:
mkdir video_conversion
cd video_conversion
bundle init
Add the required gem to your Gemfile
:
source 'https://rubygems.org'
gem 'streamio-ffmpeg', '~> 3.0.2'
Alternatively, install the gem directly via the command line:
gem install streamio-ffmpeg
Install the dependencies:
bundle install
Introduction to FFmpeg and the streamio FFmpeg gem
FFmpeg is a robust open-source tool for multimedia manipulation, allowing you to record, convert, and stream audio and video files. The Streamio FFMPEG gem offers a Ruby interface to FFmpeg, simplifying video processing integration in your applications.
Converting videos to HLS format in Ruby
Below is a Ruby class to handle video conversion to HLS. Note that the code includes enhanced error handling and updated transcoding options for better reliability.
Create a new file called video_converter.rb
:
require 'streamio-ffmpeg'
require 'fileutils'
class VideoConverter
def initialize(input_file)
begin
@movie = FFMPEG::Movie.new(input_file)
raise "Invalid file" unless @movie.valid?
@input_file = input_file
rescue FFMPEG::Error => e
raise "FFmpeg error: #{e.message}"
rescue StandardError => e
raise "Error initializing converter: #{e.message}"
end
end
def to_hls(output_dir)
FileUtils.mkdir_p(output_dir)
variants = [
{ resolution: '1280x720', video_bitrate: '2500k', audio_bitrate: '128k' },
{ resolution: '854x480', video_bitrate: '1500k', audio_bitrate: '96k' },
{ resolution: '640x360', video_bitrate: '800k', audio_bitrate: '64k' }
]
begin
variants.each do |variant|
options = {
video_codec: 'libx264',
audio_codec: 'aac',
frame_rate: 30,
resolution: variant[:resolution],
video_bitrate: variant[:video_bitrate],
audio_bitrate: variant[:audio_bitrate],
custom: [
'-hls_time', '10',
'-hls_playlist_type', 'vod',
'-hls_segment_filename', "#{output_dir}/#{variant[:resolution]}_%03d.ts"
]
}
@movie.transcode("#{output_dir}/#{variant[:resolution]}.m3u8", options) do |progress|
puts "Progress for #{variant[:resolution]}: #{(progress * 100).round}%"
end
end
generate_master_playlist(output_dir, variants)
rescue FFMPEG::Error => e
raise "Transcoding error: #{e.message}"
rescue StandardError => e
raise "General error: #{e.message}"
end
end
def to_dash(output_dir)
FileUtils.mkdir_p(output_dir)
begin
options = {
video_codec: 'libx264',
audio_codec: 'aac',
frame_rate: 30,
custom: [
'-use_template', '1',
'-use_timeline', '1',
'-seg_duration', '10',
'-adaptation_sets', 'id=0,streams=v id=1,streams=a',
'-f', 'dash'
]
}
@movie.transcode("#{output_dir}/manifest.mpd", options) do |progress|
puts "DASH conversion progress: #{(progress * 100).round}%"
end
rescue FFMPEG::Error => e
raise "DASH conversion error: #{e.message}"
rescue StandardError => e
raise "General error: #{e.message}"
end
end
private
def generate_master_playlist(output_dir, variants)
master_playlist = "#EXTM3U\n#EXT-X-VERSION:3\n"
variants.each do |variant|
bandwidth = variant[:video_bitrate].to_i * 1000
master_playlist += <<~PLAYLIST
#EXT-X-STREAM-INF:BANDWIDTH=#{bandwidth},RESOLUTION=#{variant[:resolution]}
#{variant[:resolution]}.m3u8
PLAYLIST
end
File.write("#{output_dir}/master.m3u8", master_playlist)
end
end
Converting videos to MPEG-DASH in Ruby
Enhance the VideoConverter
class to support MPEG-DASH conversion. The method below demonstrates
the process:
def to_dash(output_dir)
FileUtils.mkdir_p(output_dir)
begin
options = {
video_codec: 'libx264',
audio_codec: 'aac',
frame_rate: 30,
custom: [
'-use_template', '1',
'-use_timeline', '1',
'-seg_duration', '10',
'-adaptation_sets', 'id=0,streams=v id=1,streams=a',
'-f', 'dash'
]
}
@movie.transcode("#{output_dir}/manifest.mpd", options) do |progress|
puts "DASH conversion progress: #{(progress * 100).round}%"
end
rescue FFMPEG::Error => e
raise "DASH conversion error: #{e.message}"
rescue StandardError => e
raise "General error: #{e.message}"
end
end
Using the converter
Create a script to utilize the VideoConverter
class. This example demonstrates converting a video
file to both HLS and MPEG-DASH formats:
# Convert.rb
require_relative 'video_converter'
begin
converter = VideoConverter.new('input.mp4')
puts 'Converting to HLS...'
converter.to_hls('output/hls')
puts 'Converting to MPEG-DASH...'
converter.to_dash('output/dash')
puts 'Conversion completed successfully'
rescue StandardError => e
puts "Error: #{e.message}"
exit 1
end
Testing the streams
To test the generated streams, use Video.js, a popular HTML5 video player:
<!DOCTYPE html>
<html>
<head>
<title>Video Player</title>
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
</head>
<body>
<video
id="player"
class="video-js vjs-default-skin"
controls
preload="auto"
width="640"
height="360"
>
<source src="output/hls/master.m3u8" type="application/x-mpegURL" />
<source src="output/dash/manifest.mpd" type="application/dash+xml" />
</video>
<script>
var player = videojs('player')
</script>
</body>
</html>
Advanced error handling
For production environments, ensure robust error handling. Wrap transcoding operations in detailed error checks to capture and log issues. For example:
begin
movie = FFMPEG::Movie.new(input_file)
raise "Invalid file" unless movie.valid?
movie.transcode(output_file, options) do |progress|
puts "Progress: #{(progress * 100).round}%"
end
rescue FFMPEG::Error => e
puts "Transcoding error: #{e.message}"
rescue StandardError => e
puts "General error: #{e.message}"
end
In addition to error logging, monitor system resources and implement retry mechanisms for failed conversions.
Common challenges and solutions
Container format support
Ensure your input videos use widely supported container formats such as MP4 (H.264) for optimal compatibility. While FFmpeg supports diverse formats, H.264 in an MP4 container offers the best streaming performance.
Performance optimization
For large-scale video processing:
- Process videos asynchronously using background jobs.
- Utilize lower resolutions for preview generation.
- Implement caching for frequently accessed segments.
- Monitor system resources and adjust batch sizes accordingly.
Robust error handling
Include comprehensive error handling in your production code. Validate input files, monitor output directories for disk space, and log detailed error messages to ease debugging.
Conclusion
Automating video conversion with Ruby, FFmpeg, and the Streamio FFMPEG gem simplifies adaptive streaming with HLS and MPEG-DASH. By following these updated best practices and incorporating robust error handling, you can improve video delivery across devices and network conditions.
For a scalable and robust solution to handle video encoding and streaming, consider using Transloadit's video encoding service, which streamlines these conversions and enhances your workflow.