Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions lib/ffmpeg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,12 @@ def ffmpeg_binary
# Safely captures the standard output and the standard error of the ffmpeg command.
#
# @param args [Array<String>] The arguments to pass to ffmpeg.
# @param spawn [Hash] Options hash forwarded to Process.spawn
# @return [Array<String, Process::Status>] The standard output, the standard error, and the process status.
def ffmpeg_capture3(*args)
def ffmpeg_capture3(*args, spawn: nil)
logger.debug(self) { "ffmpeg -y #{Shellwords.join(args)}" }
FFMPEG::IO.capture3(ffmpeg_binary, '-y', *args)
spawn ||= {}
FFMPEG::IO.capture3(ffmpeg_binary, '-y', *args, **spawn)
end

# Starts a new ffmpeg process with the given arguments.
Expand All @@ -125,29 +127,33 @@ def ffmpeg_capture3(*args)
# to the specified block.
#
# @param args [Array<String>] The arguments to pass to ffmpeg.
# @param spawn [Hash] Options hash forwarded to Process.spawn
# @yieldparam stdin (+IO+) The standard input stream.
# @yieldparam stdout (+FFMPEG::IO+) The standard output stream.
# @yieldparam stderr (+FFMPEG::IO+) The standard error stream.
# @yieldparam wait_thr (+Thread+) The child process thread.
# @return [Process::Status, Array<IO, Thread>]
def ffmpeg_popen3(*args, &)
def ffmpeg_popen3(*args, spawn: nil, &)
logger.debug(self) { "ffmpeg -y #{Shellwords.join(args)}" }
FFMPEG::IO.popen3(ffmpeg_binary, '-y', *args, &)
spawn ||= {}
FFMPEG::IO.popen3(ffmpeg_binary, '-y', *args, **spawn, &)
end

# Execute a ffmpeg command.
#
# @param args [Array<String>] The arguments to pass to ffmpeg.
# @param reporters [Array<Class<FFMPEG::Reporters::Output>>] The reporters to use to parse the output.
# @param spawn [Hash] Options hash forwarded to Process.spawn
# @yield [report] Reports from the ffmpeg command (see FFMPEG::Reporters).
# @return [FFMPEG::Status]
def ffmpeg_execute(*args, status: nil, reporters: nil, timeout: nil)
def ffmpeg_execute(*args, status: nil, reporters: nil, timeout: nil, spawn: nil)
status ||= FFMPEG::Status.new
reporters ||= self.reporters
timeout ||= self.timeout

status.bind! do
ffmpeg_popen3(*args) do |_stdin, stdout, stderr, wait_thr|
spawn ||= {}
ffmpeg_popen3(*args, spawn:) do |_stdin, stdout, stderr, wait_thr|
Timeout.timeout(timeout) do
stderr.each(chomp: true) do |line|
reporter = reporters.find { |r| r.match?(line) }
Expand All @@ -171,10 +177,11 @@ def ffmpeg_execute(*args, status: nil, reporters: nil, timeout: nil)
#
# @param args [Array<String>] The arguments to pass to ffmpeg.
# @param reporters [Array<FFMPEG::Reporters::Output>] The reporters to use to parse the output.
# @param spawn [Hash] Options hash forwarded to Process.spawn
# @yield [report] Reports from the ffmpeg command (see FFMPEG::Reporters).
# @return [FFMPEG::Status]
def ffmpeg_execute!(*args, status: nil, reporters: nil, timeout: nil)
ffmpeg_execute(*args, status:, reporters:, timeout:).assert!
def ffmpeg_execute!(*args, status: nil, reporters: nil, timeout: nil, spawn: nil)
ffmpeg_execute(*args, status:, reporters:, timeout:, spawn:).assert!
end

# Get the path to the ffprobe binary.
Expand Down Expand Up @@ -205,9 +212,10 @@ def ffprobe_binary=(path)
# @param args [Array<String>] The arguments to pass to ffprobe.
# @return [Array<String, Process::Status>] The standard output, the standard error, and the process status.
# @raise [Errno::ENOENT] If the ffprobe binary cannot be found.
def ffprobe_capture3(*args)
def ffprobe_capture3(*args, spawn: nil)
logger.debug(self) { "ffprobe -y #{Shellwords.join(args)}" }
FFMPEG::IO.capture3(ffprobe_binary, '-y', *args)
spawn ||= {}
FFMPEG::IO.capture3(ffprobe_binary, '-y', *args, **spawn)
end

# Starts a new ffprobe process with the given arguments.
Expand All @@ -216,14 +224,16 @@ def ffprobe_capture3(*args)
# to the specified block.
#
# @param args [Array<String>] The arguments to pass to ffprobe.
# @param spawn [Hash] Options hash forwarded to Process.spawn
# @yieldparam stdin (+IO+) The standard input stream.
# @yieldparam stdout (+FFMPEG::IO+) The standard output stream.
# @yieldparam stderr (+FFMPEG::IO+) The standard error stream.
# @return [Process::Status, Array<IO, Thread>]
# @raise [Errno::ENOENT] If the ffprobe binary cannot be found.
def ffprobe_popen3(*args, &)
def ffprobe_popen3(*args, spawn: nil, &)
logger.debug(self) { "ffprobe -y #{Shellwords.join(args)}" }
FFMPEG::IO.popen3(ffprobe_binary, '-y', *args, &)
spawn ||= {}
FFMPEG::IO.popen3(ffprobe_binary, '-y', *args, **spawn, &)
end

# Cross-platform way of finding an executable in the $PATH.
Expand Down
10 changes: 5 additions & 5 deletions lib/ffmpeg/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ def extend!(io)
io.extend(FFMPEG::IO)
end

def capture3(*cmd)
*io, status = Open3.capture3(*cmd)
def capture3(*cmd, **spawn_opts)
*io, status = Open3.capture3(*cmd, **spawn_opts)
io.each(&method(:encode!))
[*io, status]
end

def popen3(*cmd, &block)
def popen3(*cmd, **spawn_opts, &block)
if block_given?
Open3.popen3(*cmd) do |*io, wait_thr|
Open3.popen3(*cmd, **spawn_opts) do |*io, wait_thr|
io = io.map(&method(:extend!))
block.call(*io, wait_thr)
rescue StandardError
Expand All @@ -47,7 +47,7 @@ def popen3(*cmd, &block)
raise
end
else
*io, wait_thr = Open3.popen3(*cmd)
*io, wait_thr = Open3.popen3(*cmd, **spawn_opts)
io = io.map(&method(:extend!))
[*io, wait_thr]
end
Expand Down
11 changes: 7 additions & 4 deletions lib/ffmpeg/media.rb
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,10 @@ def local?
# @param inargs [Array<String>] The arguments to pass before the input.
# @yield [report] Reports from the ffmpeg command (see FFMPEG::Reporters).
# @return [Process::Status]
def ffmpeg_execute(*args, inargs: [], status: nil, reporters: nil, timeout: nil, &block)
FFMPEG.ffmpeg_execute(*inargs, '-i', path, *args, status:, reporters:, timeout:, &block)
def ffmpeg_execute(*args, inargs: [], status: nil, reporters: nil, timeout: nil,
spawn: nil, &block)
FFMPEG.ffmpeg_execute(*inargs, '-i', path, *args, status:, reporters:, timeout:,
spawn:, &block)
end

# Execute a ffmpeg command with the media as input
Expand All @@ -541,8 +543,9 @@ def ffmpeg_execute(*args, inargs: [], status: nil, reporters: nil, timeout: nil,
# @param inargs [Array<String>] The arguments to pass before the input.
# @yield [report] Reports from the ffmpeg command (see FFMPEG::Reporters).
# @return [Process::Status]
def ffmpeg_execute!(*args, inargs: [], status: nil, reporters: nil, timeout: nil, &block)
ffmpeg_execute(*args, inargs:, status:, reporters:, timeout:, &block).assert!
def ffmpeg_execute!(*args, inargs: [], status: nil, reporters: nil, timeout: nil,
spawn: nil, &block)
ffmpeg_execute(*args, inargs:, status:, reporters:, timeout:, spawn:, &block).assert!
end
end
end
10 changes: 7 additions & 3 deletions lib/ffmpeg/transcoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ def initialize(
#
# @param media [String, Pathname, URI, FFMPEG::Media] The media file to transcode.
# @param output_path [String, Pathname] The output path to save the transcoded files.
# @param chdir [String, Pathname] Change the ffmpeg working directory
# @yield The block to execute to report the transcoding process.
# @return [FFMPEG::Transcoder::Status] The status of the transcoding process.
def process(media, output_path, &)
def process(media, output_path, chdir: nil, &)
status = nil

attempts = 0
Expand All @@ -126,11 +127,13 @@ def process(media, output_path, &)
end

inargs = CommandArgs.compose(media, context:, &@compose_inargs).to_a
spawn = chdir ? { chdir: } : {}
status = media.ffmpeg_execute(
*args,
inargs:,
reporters:,
timeout:,
spawn:,
status: Status.new(output_paths, checks:),
&
)
Expand All @@ -148,10 +151,11 @@ def process(media, output_path, &)
#
# @param media [String, Pathname, URI, FFMPEG::Media] The media file to transcode.
# @param output_path [String, Pathname] The output path to save the transcoded files.
# @param chdir [String, Pathname] Change the ffmpeg working directory
# @yield The block to execute to report the transcoding process.
# @return [FFMPEG::Transcoder::Status] The status of the transcoding process.
def process!(media, output_path, &)
process(media, output_path, &).assert!
def process!(media, output_path, chdir: nil, &)
process(media, output_path, chdir:, &).assert!
end
end
end
2 changes: 1 addition & 1 deletion spec/ffmpeg/media_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ module FFMPEG
block = proc {}

expect(status).to receive(:assert!).and_return(status)
expect(subject).to receive(:ffmpeg_execute).with(*args, inargs:, status:, reporters:, timeout:,
expect(subject).to receive(:ffmpeg_execute).with(*args, inargs:, status:, reporters:, timeout:, spawn: nil,
&block).and_return(status)
expect(subject.ffmpeg_execute!(*args, inargs:, status:, reporters:, timeout:, &block)).to be(status)
end
Expand Down
15 changes: 14 additions & 1 deletion spec/ffmpeg/transcoder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ module FFMPEG
expect(attempts).to eq(2)
end
end

it 'can run ffmpeg in a different working directory (chdir)' do
output_path = File.join(tmp_dir, SecureRandom.hex(4))
status = double(Transcoder::Status, success?: true)

expect(FFMPEG::IO).to receive(:popen3)
.with(any_args, chdir: tmp_dir)
.and_return(status)

subject.process(media, output_path, chdir: tmp_dir)
end
end

describe '#process!' do
Expand All @@ -127,7 +138,9 @@ module FFMPEG
block = proc {}

expect(status).to receive(:assert!).and_return(status)
expect(subject).to receive(:process).with(media, output_path, &block).and_return(status)
expect(subject).to receive(:process)
.with(media, output_path, chdir: nil, &block)
.and_return(status)
expect(subject.process!(media, output_path, &block)).to eq(status)
end
end
Expand Down
7 changes: 7 additions & 0 deletions spec/ffmpeg_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
expect(status.exitstatus).to eq(0)
end

it 'forwards spawn parameter to popen3' do
spawn = { chdir: '/tmp/test' }

expect(FFMPEG::IO).to receive(:popen3).with(any_args, chdir: '/tmp/test')
described_class.ffmpeg_execute(spawn:)
end

context 'when ffmpeg hangs' do
before do
described_class.ffmpeg_binary = fixture_file('bin/mock-ffmpeg')
Expand Down