Skip to content

Simple yet powerful ruby ffmpeg wrapper for reading metadata and transcoding movies

License

Notifications You must be signed in to change notification settings

instructure/ruby-ffmpeg

Repository files navigation

FFMPEG

Test Lint

Simple yet powerful wrapper around the ffmpeg command for reading metadata and transcoding media.

Compatibility

Ruby

Only guaranteed to work with Ruby 3.1 or later.

ffmpeg

The current gem is tested against ffmpeg 4, 5, 6 and 7. So no guarantees with earlier (or much later) versions. Output and input standards have inconveniently changed rather a lot between versions of ffmpeg. Our goal is to keep this library in sync with new versions of ffmpeg as they come along.

On macOS: brew install ffmpeg.

Installation

require 'ffmpeg', git: 'https://github.com/instructure/ruby-ffmpeg'

Usage

NOTE: For advanced usage, don't be afraid to dive into the source code. The gem is designed with care and documented all over the place, so dig in.

Metadata

media = FFMPEG::Media.new('path/to/media.mp4')

media.valid? # true (would be false if ffmpeg fails to read the media metadata)
media.local? # true (would be false if the file is remote)
media.remote? # false

media.streams # [FFMPEG::Stream, FFMPEG::Stream]
media.video_streams # [FFMPEG::Stream]
media.video_streams? # true (tells you if the media has video streams or not)
media.default_video_stream # FFMPEG::Stream
media.audio_streams # [FFMPEG::Stream]
media.audio_streams? # true (tells you if the media has audio streams or not)
media.default_audio_stream # FFMPEG::Stream
media.video? # true (tells you if the media has a movie stream – non-attached-picture)
media.audio? # false (tells you if the media is an audio file – based on the streams)

media.rotation # 90 (rotation of the default video stream in degrees)
media.raw_width # 480 (the reported width of the default video stream in pixels)
media.raw_height # 640 (the reported height of the default video stream in pixels)
media.width # 640 (width of the default video stream in pixels)
media.height # 480 (height of the default video stream in pixels)

For the full list of attributes available on the FFMPEG::Media object, see the source code.

Transcoding

The two core components of the transcoding process are the FFMPEG::Preset and FFMPEG::Transcoder classes.

The FFMPEG::Preset class

preset = FFMPEG::Preset.new(
  name: 'My Preset',
  filename: '%<basename>s.mp4',
  metadata: {
    some_important_metadata_for_you: 'foo'
  }
) do
  # This block sets up the output arguments of the ffmpeg command.
  # It uses a DSL to define the arguments in a more human-readable way.
  # The methods for the DSL are defined in the FFMPEG::RawCommandArgs and
  # FFMPEG::CommandArgs classes.

  video_codec_name 'libx264' # -c:v libx264
  audio_codec_name 'aac' # -c:a aac
  
  # media is the FFMPEG::Media object used as input for the transcoding process
  map media.video_mapping_id do # -map v:0
    filter FFMPEG::Filters.scale(width: -2, height: 360) # -vf scale=w=-2:h=360
    constant_rate_factor 22 # -crf 22
    frame_rate 30 # -r 30
  end

  # media is the FFMPEG::Media object used as input for the transcoding process
  map media.audio_mapping_id do # -map a:0
    audio_bit_rate 128000 # -b:a 128k
  end
end

The FFMPEG::Transcoder class

transcoder = FFMPEG::Transcoder.new(
  name: 'My Transcoder',
  metadata: {
    some_important_metadata_for_you: 'bar'
  },
  # You can pass multiple presets to the transcoder
  # and they will be processed in a single ffmpeg command
  # to optimize the transcoding process.
  presets: [preset],
  # The reporters are used to generate reports during the transcoding process.
  reporters: [FFMPEG::Reporters::Progress, FFMPEG::Reporters::Silence]
) do
  # This block sets up the input arguments of the ffmpeg command.
  # It uses the same DSL to define the arguments as the preset does for the output arguments.
  # The methods for the DSL are defined in the FFMPEG::RawCommandArgs and
  # FFMPEG::CommandArgs classes.

  # media is the FFMPEG::Media object used as input for the transcoding process
  if media.rotated?
    raw_arg '-noautorotate' # -noautorotate
  end
end

status = transcoder.process(media, '/path/to/output') do |report|
  # This block is called for each report generated by the reporters
  case report.class
  when FFMPEG::Reporters::Progress
    puts "Progress: #{report.time}"
  when FFMPEG::Reporters::Silence
    puts "Silence: #{report.duration}"
  else
    # FFMPEG::Reporters::Output
  end
end

status.success? # true (would be false if ffmpeg fails to transcode the media)
status.exitstatus # 0 (the exit status of the ffmpeg command)
status.paths # ['/path/to/output.mp4'] (the paths of the output files)
status.media # [FFMPEG::Media] (the media objects of the output files)

Presets

The gem comes with a few presets out of the box.

You can find them in the lib/ffmpeg/presets directory.

Customization

By default, the gem assumes that the ffmpeg and ffprobe binaries are available in the execution path (named ffmpeg and ffprobe) and so will run commands that look something like ffmpeg -i /path/to/input.file .... Use the below setters to specify the full path to the binaries if necessary:

FFMPEG.ffmpeg_binary = '/usr/local/bin/ffmpeg'
FFMPEG.ffprobe_binary = '/usr/local/bin/ffprobe'

By default, the gem will wait for 30 seconds between IO feedback from the FFMPEG process. After which an error is raised and the process killed. It is possible to modify this behaviour by setting a new default:

# Change the IO timeout
FFMPEG.io_timeout = 10

# Disable the IO timeout altogether
FFMPEG.io_timeout = nil

The gem uses a logger to output debug information. By default, it will log to STDOUT, but you can change this behaviour by setting a new logger:

FFMPEG.logger = Logger.new('path/to/logfile.log')

Copyright

Copyright (c) Instructure, Inc. See LICENSE for details.

About

Simple yet powerful ruby ffmpeg wrapper for reading metadata and transcoding movies

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages