Skip to content

Instantly share code, notes, and snippets.

@ayosec
Created December 22, 2014 00:22
Show Gist options
  • Save ayosec/d71409c6a99cd51d3ead to your computer and use it in GitHub Desktop.
Save ayosec/d71409c6a99cd51d3ead to your computer and use it in GitHub Desktop.
Extract fragments from a video using FFmpeg
#!/usr/bin/ruby
require "tmpdir"
def usage()
STDERR.puts <<EOU
Usage: #$0 input-file output-file ranges+
Ranges can be defined with:
start:end Define a range in absolute times
start+duration +duration seconds from start
start: From start to the end
:duration Only the first 'duration' seconds
EOU
exit 1
end
args = ARGV.dup
if args.size < 3
usage()
end
input_file = args.shift
input_file_ext = File.extname(input_file)
output_file = args.shift
if File.exist?(output_file)
STDERR.puts "#{output_file} exists"
exit 2
end
ranges = args.map do |range|
case range
when /^(\d+):(\d+)$/
start = $1.to_i
end_ = $2.to_i
if end_ < start
STDERR.puts "Invalid value in #{range}"
usage()
end
{ start: start, duration: end_ - start }
when /^:(\d+)$/
duration = $1.to_i
{ duration: duration }
when /^(\d+):$/
start = $1.to_i
{ start: start }
when /^(\d+)\+(\d+)$/
start = $1.to_i
duration = $2.to_i
{ start: start, duration: duration }
else
STDERR.puts "\033[31mInvalid range format '#{range}'\033[m"
usage()
end
end
dir = Dir.mktmpdir("video-extract--")
puts "Temp files in #{dir}"
running = []
partial_videos = []
ranges.each_with_index do |range, index|
output_name = "#{dir}/video#{index}#{input_file_ext}"
stdout_name = "#{dir}/video#{index}.log"
partial_videos << output_name
running << fork do
s, d = range.values_at(:start, :duration)
ffmpeg_args = ["ffmpeg", "-y"]
# Start
ffmpeg_args << "-ss" << s.to_s if s
# Input file
ffmpeg_args << "-i" << input_file
# End
ffmpeg_args << "-t" << d.to_s if d
# Default "copy" codec
ffmpeg_args << "-c" << "copy"
# Output
ffmpeg_args << output_name
puts "Running #{ffmpeg_args.join(" ")} > #{stdout_name}"
STDIN.reopen("/dev/null", "r")
STDOUT.reopen(stdout_name, "a")
STDERR.reopen(stdout_name, "a")
exec(*ffmpeg_args)
end
end
running.each do |pid|
Process.waitpid(pid)
end
concat_file = "#{dir}/concat"
File.open(concat_file, "w") do |concat|
partial_videos.each do |filename|
concat.puts "file '#{filename}'"
end
end
cmd = "ffmpeg", "-f", "concat", "-i", concat_file, "-c", "copy", output_file
puts "Running #{cmd.join(" ")}"
system(*cmd)
puts "Removing intermediate videos..."
partial_videos.each do |filename|
File.unlink(filename)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment