Last updated: February 5, 2025

<span aria-hidden="true" id="streaming-made-easy-convert-videos-to-hls-and-mpeg-dash-with-ruby"></span>

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

![Kevin van Zonneveld](/assets/images/teammates/avatar-kvz-4.jpg?dpl=dpl_3fBRD5jmFtSDABLXGJJyXU1nVXf8)

**Kevin van Zonneveld**

Co-founder · Amsterdam, The Netherlands · Show bio

[](https://x.com/kvz)[](https://github.com/kvz)

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.

<span aria-hidden="true" id="introduction-to-hls-and-mpeg-dash"></span>

## 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.

<span aria-hidden="true" id="setting-up-your-ruby-environment-for-video-conversion"></span>

## 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:

```bash
ruby -v

```

If you need to install or upgrade Ruby, use a version manager like [RVM⁠](https://rvm.io/) or[rbenv⁠](https://github.com/rbenv/rbenv).

Next, install FFmpeg, which is essential for processing video files.

For macOS users:

```bash
brew install ffmpeg

```

For Ubuntu/Debian users:

```bash
sudo apt-get update
sudo apt-get install ffmpeg

```

Create a new project directory and initialize it:

```bash
mkdir video_conversion
cd video_conversion
bundle init

```

Add the required gem to your `Gemfile`:

```ruby
source 'https://rubygems.org'

gem 'streamio-ffmpeg', '~> 3.0.2'

```

Alternatively, install the gem directly via the command line:

```bash
gem install streamio-ffmpeg

```

Install the dependencies:

```bash
bundle install

```

<span aria-hidden="true" id="introduction-to-ffmpeg-and-the-streamio-ffmpeg-gem"></span>

## 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.

<span aria-hidden="true" id="converting-videos-to-hls-format-in-ruby"></span>

## 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`:

```ruby
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

```

<span aria-hidden="true" id="converting-videos-to-mpeg-dash-in-ruby"></span>

## Converting videos to MPEG-DASH in Ruby

Enhance the `VideoConverter` class to support MPEG-DASH conversion. The method below demonstrates the process:

```ruby
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

```

<span aria-hidden="true" id="using-the-converter"></span>

## 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:

```ruby
# 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

```

<span aria-hidden="true" id="testing-the-streams"></span>

## Testing the streams

To test the generated streams, use Video.js, a popular HTML5 video player:

```html
<!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>

```

<span aria-hidden="true" id="advanced-error-handling"></span>

## Advanced error handling

For production environments, ensure robust error handling. Wrap transcoding operations in detailed error checks to capture and log issues. For example:

```ruby
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.

<span aria-hidden="true" id="common-challenges-and-solutions"></span>

## Common challenges and solutions

<span aria-hidden="true" id="container-format-support"></span>

### 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.

<span aria-hidden="true" id="performance-optimization"></span>

### Performance optimization

For large-scale video processing:

1. Process videos asynchronously using background jobs.
2. Utilize lower resolutions for preview generation.
3. Implement caching for frequently accessed segments.
4. Monitor system resources and adjust batch sizes accordingly.

<span aria-hidden="true" id="robust-error-handling"></span>

### 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.

<span aria-hidden="true" id="conclusion"></span>

## 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](/services/video-encoding.md), which streamlines these conversions and enhances your workflow.

\#ruby#video-transcoding#ffmpeg#video-encoding-service

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
