Replace streamio-ffmpeg library.
Replace the streamio-ffmpeg library with our own very thin FFmpeg wrapper.
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -16,7 +16,6 @@ gem 'bcrypt', :require => "bcrypt"
|
|||||||
gem 'capistrano', '~> 3.10'
|
gem 'capistrano', '~> 3.10'
|
||||||
gem 'capistrano-rails'
|
gem 'capistrano-rails'
|
||||||
gem 'capistrano-rbenv'
|
gem 'capistrano-rbenv'
|
||||||
gem 'streamio-ffmpeg'
|
|
||||||
gem 'rubyzip', :require => "zip"
|
gem 'rubyzip', :require => "zip"
|
||||||
gem 'stripe'
|
gem 'stripe'
|
||||||
gem 'aws-sdk-sqs', '~> 1'
|
gem 'aws-sdk-sqs', '~> 1'
|
||||||
|
|||||||
@@ -458,8 +458,6 @@ GEM
|
|||||||
net-scp (>= 1.1.2)
|
net-scp (>= 1.1.2)
|
||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
stackprof (0.2.17)
|
stackprof (0.2.17)
|
||||||
streamio-ffmpeg (3.0.2)
|
|
||||||
multi_json (~> 1.8)
|
|
||||||
stripe (5.38.0)
|
stripe (5.38.0)
|
||||||
stripe-ruby-mock (3.0.1)
|
stripe-ruby-mock (3.0.1)
|
||||||
dante (>= 0.2.0)
|
dante (>= 0.2.0)
|
||||||
@@ -580,7 +578,6 @@ DEPENDENCIES
|
|||||||
simplecov
|
simplecov
|
||||||
solargraph
|
solargraph
|
||||||
stackprof
|
stackprof
|
||||||
streamio-ffmpeg
|
|
||||||
stripe
|
stripe
|
||||||
stripe-ruby-mock
|
stripe-ruby-mock
|
||||||
terminal-table
|
terminal-table
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
|
require "shellwords"
|
||||||
|
|
||||||
|
# A wrapper for the ffmpeg command.
|
||||||
class FFmpeg
|
class FFmpeg
|
||||||
|
extend Memoist
|
||||||
|
|
||||||
|
class Error < StandardError; end
|
||||||
|
|
||||||
attr_reader :file
|
attr_reader :file
|
||||||
|
|
||||||
# Operate on a file with FFmpeg.
|
# Operate on a file with FFmpeg.
|
||||||
@@ -16,10 +23,52 @@ class FFmpeg
|
|||||||
|
|
||||||
# https://ffmpeg.org/ffmpeg.html#Main-options
|
# https://ffmpeg.org/ffmpeg.html#Main-options
|
||||||
# https://ffmpeg.org/ffmpeg-filters.html#thumbnail
|
# https://ffmpeg.org/ffmpeg-filters.html#thumbnail
|
||||||
ffmpeg_out, status = Open3.capture2e("ffmpeg -i #{file.path} -vf thumbnail=300 -frames:v 1 -y #{vp.path}")
|
output = shell!("ffmpeg -i #{file.path.shellescape} -vf thumbnail=300 -frames:v 1 -y #{vp.path.shellescape}")
|
||||||
raise "ffmpeg failed: #{ffmpeg_out}" if !status.success?
|
Rails.logger.debug(output)
|
||||||
Rails.logger.debug(ffmpeg_out)
|
|
||||||
|
|
||||||
MediaFile.open(vp)
|
MediaFile.open(vp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get file metadata using ffprobe.
|
||||||
|
# @see https://ffmpeg.org/ffprobe.html
|
||||||
|
# @see https://gist.github.com/nrk/2286511
|
||||||
|
# @return [Hash] a hash of the file's metadata
|
||||||
|
def metadata
|
||||||
|
output = shell!("ffprobe -v quiet -print_format json -show_format -show_streams #{file.path.shellescape}")
|
||||||
|
json = JSON.parse(output)
|
||||||
|
json.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
def width
|
||||||
|
video_channels.first[:width]
|
||||||
|
end
|
||||||
|
|
||||||
|
def height
|
||||||
|
video_channels.first[:height]
|
||||||
|
end
|
||||||
|
|
||||||
|
def duration
|
||||||
|
metadata.dig(:format, :duration).to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
def video_channels
|
||||||
|
metadata[:streams].select { |stream| stream[:codec_type] == "video" }
|
||||||
|
end
|
||||||
|
|
||||||
|
def audio_channels
|
||||||
|
metadata[:streams].select { |stream| stream[:codec_type] == "audio" }
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_audio?
|
||||||
|
audio_channels.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def shell!(command)
|
||||||
|
program = command.shellsplit.first
|
||||||
|
output, status = Open3.capture2e(command)
|
||||||
|
raise Error, "#{program} failed: #{output}" if !status.success?
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
memoize :metadata
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,14 +3,12 @@
|
|||||||
#
|
#
|
||||||
# @see https://github.com/streamio/streamio-ffmpeg
|
# @see https://github.com/streamio/streamio-ffmpeg
|
||||||
class MediaFile::Video < MediaFile
|
class MediaFile::Video < MediaFile
|
||||||
|
delegate :duration, :has_audio?, to: :video
|
||||||
|
|
||||||
def dimensions
|
def dimensions
|
||||||
[video.width, video.height]
|
[video.width, video.height]
|
||||||
end
|
end
|
||||||
|
|
||||||
def duration
|
|
||||||
video.duration
|
|
||||||
end
|
|
||||||
|
|
||||||
def preview(max_width, max_height)
|
def preview(max_width, max_height)
|
||||||
preview_frame.preview(max_width, max_height)
|
preview_frame.preview(max_width, max_height)
|
||||||
end
|
end
|
||||||
@@ -19,20 +17,14 @@ class MediaFile::Video < MediaFile
|
|||||||
preview_frame.crop(max_width, max_height)
|
preview_frame.crop(max_width, max_height)
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_audio?
|
|
||||||
video.audio_channels.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def video
|
def video
|
||||||
raise NotImplementedError, "can't process videos: ffmpeg or mkvmerge not installed" unless self.class.videos_enabled?
|
FFmpeg.new(file)
|
||||||
|
|
||||||
FFMPEG::Movie.new(file.path)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def preview_frame
|
def preview_frame
|
||||||
FFmpeg.new(file).smart_video_preview
|
video.smart_video_preview
|
||||||
end
|
end
|
||||||
|
|
||||||
memoize :video, :preview_frame, :dimensions, :duration, :has_audio?
|
memoize :video, :preview_frame, :dimensions, :duration, :has_audio?
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
unless Rails.env.development?
|
|
||||||
FFMPEG.logger.level = Logger::ERROR
|
|
||||||
end
|
|
||||||
BIN
test/files/test-audio.mp4
Normal file
BIN
test/files/test-audio.mp4
Normal file
Binary file not shown.
@@ -170,4 +170,11 @@ class MediaFileTest < ActiveSupport::TestCase
|
|||||||
assert_equal([60, 60], webm.dimensions)
|
assert_equal([60, 60], webm.dimensions)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "for a video" do
|
||||||
|
should "detect videos with audio" do
|
||||||
|
assert_equal(true, MediaFile.open("test/files/test-audio.mp4").has_audio?)
|
||||||
|
assert_equal(false, MediaFile.open("test/files/test-300x300.mp4").has_audio?)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user