/usr/bin/ct2-make-movie is in ctioga2 0.10-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | #!/usr/bin/ruby
=begin rdoc
This program is copyright 2014 by Vincent Fourmond.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=end
# This program is a wrapper around ctioga2 to make it easy to make
# movies.
require 'open3'
require 'fileutils'
require 'optparse'
require 'shellwords'
# Path to ctioga2 executable
ct2 = "ctioga2"
tmpdir = "tmp"
# The class in charge of running ffmpeg
class EncodingJob
# Target file
attr_accessor :target
# Bitrate
attr_accessor :bitrate
# Video codec
attr_accessor :codec
# Extra arguments
attr_accessor :extra_args
# Writes the given frame for encoding
def write_frame(data)
@encoder.write(data)
end
def add_args(args)
@extra_args ||= []
@extra_args += args
end
# Start job
def start_job(size)
ffmpeg_args = ["ffmpeg", '-y', "-f", "rawvideo",
"-r", "25", "-s", size, "-i", "-"]
if @bitrate
ffmpeg_args << "-b" << @bitrate
end
if @codec
ffmpeg_args << "-vcodec" << @codec
end
ffmpeg_args += @extra_args if @extra_args
ffmpeg_args << @target
p ffmpeg_args
@encoder = IO::popen(ffmpeg_args, "wb")
end
def close()
@encoder.close
end
end
DimensionConversion = {
"pt" => (72.0/72.27),
"bp" => 1.0,
"in" => 72.0,
"cm" => (72.0/2.54),
"mm" => (72.0/25.4),
}
def dim_to_points(dim)
if dim =~ /^\s*(\d+(\.\d*)?)\s*(pt|bp|cm|in|mm)\s*$/i
return $1.to_f * DimensionConversion[$3.downcase]
else
raise "Invalid dimension: #{dim}"
end
end
# Converts the given "real-size" resolution into postscript points
def page_size_to_points(spec)
if spec =~ /(.*)x(.*)/i
return [dim_to_points($1), dim_to_points($2)]
else
raise "Invalid page size: #{spec}"
end
end
# @todo Build a movie from a list of PDF files. Maybe less error
# checking than directly.
# The target resolution
res = [600,600]
# The target page size (in bp)
ct2_size = nil
# The corresponding number of points
# The conversion factor (between points and inches)
conv = 250.0
# The oversampling factor (to get something smooth in the end)
oversampling = 2
# Whether we use pdftoppm or not for the conversion. Much faster than
# convert
use_pdftoppm = false
# Whether we keep all intermediate PDF files, or we reuse the same
# file over and over again.
store_all = true
encoders = [ EncodingJob.new ]
cur_enc = encoders.first
opts = OptionParser.new do |opts|
opts.banner = "Usage: #$0 [options] file.ct2 arguments..."
##################################################
# Encoding-related options
opts.on("-t", "--target FILE", "Target video file") do |t|
if cur_enc.target
cur_enc = EncodingJob.new
encoders << cur_enc
end
cur_enc.target = t
end
opts.on("-b", "--bitrate RATE", "Bitrate (indicative)") do |v|
cur_enc.bitrate = v
end
opts.on("", "--codec CODEC", "Target codec") do |v|
cur_enc.codec = v
end
opts.on("", "--ffmpeg-args ARGS", "Extra ffmpeg args") do |v|
cur_enc.add_args(Shellwords.split(v))
end
##################################################
#
opts.on("", "--dir DIR", "Temporary directory for storage") do |t|
tmpdir = t
end
opts.on("", "--version", "Prints version string") do
puts "0.1"
end
opts.on("-p", "--[no-]pdftoppm", "Whether or not to use pdftoppm") do |t|
use_pdftoppm = t
end
opts.on("-r", "--page-size SIZE",
"Set ctioga2 page size (in TeX dimensions)") do |v|
ct2_size = page_size_to_points(v)
p ct2_size
end
opts.on("", "--resolution RES",
"Set target resolution (overridden to some extent by page-size)") do |r|
r =~ /(\d+)x(\d+)/
res = [$1.to_f, $2.to_f]
end
opts.on("", "--[no-]store", "To store all or not..") do |v|
store_all = v
end
end
opts.parse!(ARGV)
# First, we choose the target page size and resolution.
if ct2_size
ct2_page_size = ct2_size.map { |x| "#{x}bp" }.join("x")
# maintain aspect ratio
res[1] = res[0] * ct2_size[1]/ct2_size[0]
conv = res[0]/ct2_size[0] * 72.27
else
ct2_page_size = res.map { |x| "#{x/conv}in"}.join("x")
end
size = res.map { |x| "#{x.to_i}"}.join("x")
puts "Producing #{ct2_page_size} PDF and converting to #{size} for the video"
file = ARGV.shift
cur_enc.target ||= file.sub(/(\.ct2)?$/, ".avi")
args = []
for a in ARGV
# Expansion !
if a =~ /^(.*)\.\.(.*):(\d+)\s*$/
s = $1.to_f
e = $2.to_f
nb = $3.to_i
nb.times do |i|
args << "#{s + (e-s)*i/(nb-1.0)}"
end
else
args << a
end
end
FileUtils::mkpath(tmpdir)
# Now, we compute the ctioga2 real size
# @todo Use other encoding programs !
# Start all encoders
for enc in encoders
enc.start_job(size)
end
format = if store_all
"#{tmpdir}/file-%04d"
else
"#{tmpdir}/file"
end
index = 0
for f in args
name = format % index
ct2_cmdline = [ct2,
"--set", "arg", f,
"--set", "index", "#{index}",
"-f", file, "--name", name, "-r", ct2_page_size]
puts "Running: #{ct2_cmdline.join(" ")}"
system(*ct2_cmdline)
b = nil
if use_pdftoppm
b1, s = Open3.capture2(
"pdftoppm",
"-r",
"#{(conv*oversampling).to_i}",
"#{name}.pdf",
:stdin_data=>"", :binmode=>true)
b, s = Open3.capture2("convert",
"PPM:-",
"-resize", "#{size}!",
"-depth", "8", "YUV:-",
:stdin_data=>b1, :binmode=>true)
else
b, s = Open3.capture2("convert",
"-density", "#{(conv*oversampling).to_i}",
"#{name}.pdf",
"-alpha", "Remove",
"-resize", "#{size}!",
"-depth", "8", "YUV:-",
:stdin_data=>"", :binmode=>true)
end
for enc in encoders
enc.write_frame(b)
end
index += 1
end
for enc in encoders
enc.close
end
|