Skip to content
This repository was archived by the owner on Apr 5, 2022. It is now read-only.

Commit fd0f79e

Browse files
author
Doug Anarino
committed
Version 4.0.18 - blend filter
1 parent bb5192e commit fd0f79e

File tree

11 files changed

+190
-137
lines changed

11 files changed

+190
-137
lines changed

Changelog

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
version <future>:
33
- freeze frame
44

5+
version 4.0.18:
6+
- support added for blend filter
7+
- stopped using movie filter in favor of indexed video labels
8+
- added support for pixel_format output option
9+
510
version 4.0.17:
611
- now using labeled inputs instead of movie filter in filtergraphs
712
- fixed issues with image output in ffmpeg 3 (don't include video_rate)

lib/graph.rb

+33-28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
module MovieMasher
33
# base class for most other graph related classes
44
class GraphUtility
5+
def __join_commands(cmds)
6+
joined_commands = []
7+
cmds = cmds.reject(&:empty?)
8+
c = cmds.length
9+
c.times do |i|
10+
cmd = cmds[i]
11+
cmd = "#{cmd}," unless (i.zero? && cmd.end_with?(':v]')) || i == c - 1
12+
joined_commands << cmd
13+
end
14+
joined_commands.join
15+
end
516
def __coerce_if_numeric(value)
617
Evaluate.coerce_if_numeric(value)
718
end
@@ -49,6 +60,7 @@ def duration
4960
@render_range.length_seconds
5061
end
5162
def graph_command(output)
63+
FilterSourceRaw.input_index = 0
5264
@job_output = output
5365
end
5466
def graph_scope
@@ -93,17 +105,13 @@ def initialize(id = nil)
93105
# base for all filter chains
94106
class Chain < GraphUtility
95107
def chain_command(scope)
96-
cmds = []
97-
@filters.each do |filter|
98-
cmd =
99-
if filter.is_a?(Filter)
100-
filter.filter_command(scope)
101-
else
102-
filter.chain_command(scope)
103-
end
104-
cmds << cmd unless cmd.to_s.empty?
108+
cmds = @filters.map do |f|
109+
f.send(f.is_a?(Filter) ? :filter_command : :chain_command, scope)
105110
end
106-
cmds.join(',')
111+
__join_commands(cmds)
112+
end
113+
def chain_labels(label, i)
114+
"[#{label}#{1 == i ? '' : 'ed'}#{i - 1}][#{label}#{i}]"
107115
end
108116
def initialize(input = nil, job_input = nil)
109117
@input = input
@@ -132,7 +140,12 @@ def initialize(input, job_input)
132140
def initialize_chains
133141
if @input[:merger]
134142
@input[:merger][:dimensions] ||= @input[:dimensions]
135-
@merger_chain = ChainModule.new(@input[:merger], @job_input, @input)
143+
@merger_chain =
144+
if 'com.moviemasher.merger.blend' == @input[:merger][:id]
145+
ChainBlend.new(@input[:merger], @job_input, @input)
146+
else
147+
ChainModule.new(@input[:merger], @job_input, @input)
148+
end
136149
else
137150
@merger_chain = ChainOverlay.new(@job_input)
138151
end
@@ -151,12 +164,7 @@ def inputs
151164
end
152165
def layer_command(scope)
153166
layer_scope(scope)
154-
cmds = []
155-
@chains.each do |chain|
156-
chain_cmd = chain.chain_command(scope)
157-
cmds << chain_cmd unless chain_cmd.to_s.empty?
158-
end
159-
cmds.join(',')
167+
__join_commands(@chains.map { |chain| chain.chain_command(scope) })
160168
end
161169
def layer_scope(scope)
162170
__raise_unless(@input[:length], "no input length #{@input}")
@@ -166,8 +174,10 @@ def layer_scope(scope)
166174
scope[:overlay_w], scope[:overlay_h] = @input[:dimensions].split('x')
167175
end
168176
end
169-
def merger_command(scope)
170-
@merger_chain.chain_command(scope)
177+
def merger_command(scope, label, i)
178+
merge_cmd = @merger_chain.chain_command(scope)
179+
__raise_if_empty(merge_cmd, "merger produced nothing #{self}")
180+
"#{@merger_chain.chain_labels(label, i)}#{merge_cmd}"
171181
end
172182
def range
173183
(@input ? @input[:range] : nil)
@@ -177,8 +187,6 @@ def trim_command(render_range)
177187
# puts "command_range_trim #{input_range}"
178188
cmd = ''
179189
if render_range && input_range && !input_range.equals?(render_range)
180-
# puts "render_range #{render_range.inspect}"
181-
# puts "input_range #{input_range.inspect}"
182190
range_start = render_range.start_seconds
183191
range_end = render_range.end_seconds
184192
input_start = input_range.start_seconds
@@ -204,8 +212,9 @@ def add_new_layer(input)
204212
@layers << layer
205213
layer
206214
end
207-
def graph_command(*)
208-
super
215+
def graph_command(output, dont_set_input_index = false) # LayerTransition
216+
FilterSourceRaw.input_index = 0 unless dont_set_input_index
217+
@job_output = output
209218
graph_cmds = []
210219
layer_length = @layers.length
211220
layer_length.times do |i|
@@ -218,11 +227,7 @@ def graph_command(*)
218227
if 1 < layer_length
219228
(1..layer_length - 1).each do |i|
220229
layer = @layers[i]
221-
cmd = (1 == i ? "[#{@label_name}0]" : "[#{@label_name}ed#{i - 1}]")
222-
cmd += "[#{@label_name}#{i}]"
223-
merge_cmd = layer.merger_command(graph_scope)
224-
__raise_if_empty(merge_cmd, "merger produced nothing #{layer}")
225-
cmd += merge_cmd
230+
cmd = layer.merger_command(graph_scope, @label_name, i)
226231
cmd += "[#{@label_name}ed#{i}]" if i + 1 < layer_length
227232
graph_cmds << cmd
228233
end

lib/graphs/chains.rb

+14-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ def initialize_filters
1515
class ChainModule < Chain
1616
attr_writer :input
1717
def chain_command(scope)
18-
scope = input_scope(scope)
19-
super
18+
super(input_scope(scope))
2019
end
2120
def initialize(mod_input, mash_input, applied_input)
2221
# applied_input is same as mod_input for themes
@@ -149,4 +148,17 @@ def __pad_filter(w_scaled, h_scaled, orig_w_f, orig_h_f)
149148
)
150149
end
151150
end
151+
# a merger chain supporting blend filter
152+
class ChainBlend < ChainModule
153+
def chain_labels(label, i)
154+
label_1 = "#{label}#{i}"
155+
label_2 = "#{label}#{1 == i ? '' : 'ed'}#{i - 1}"
156+
cmds = []
157+
cmds << "[#{label_1}]format=pix_fmts=rgba[#{label_1}_rgba]"
158+
cmds << "[#{label_2}]format=pix_fmts=rgba[#{label_2}_rgba]"
159+
cmds << "[#{label_1}_rgba][#{label_2}_rgba]"
160+
puts "CHAIN_LABELS #{cmds.join(';')}"
161+
cmds.join(';')
162+
end
163+
end
152164
end

lib/graphs/filters.rb

+28-4
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def initialize(expr = 'PTS-STARTPTS')
236236
super('setpts', expr: expr)
237237
end
238238
end
239-
# raw input source
239+
# base filter source
240240
class FilterSource < FilterHash
241241
def filter_command(scope)
242242
cmd = super
@@ -250,17 +250,41 @@ def initialize(id, hash, dimensions = nil)
250250
@dimensions = dimensions
251251
end
252252
end
253-
# video source
254-
class FilterSourceMovie < FilterSource
253+
# base raw source
254+
class FilterSourceRaw < FilterSource
255+
class << self
256+
attr_accessor :input_index
257+
end
258+
FilterSourceRaw.input_index = 0
255259
def filter_name
256260
"#{super} #{File.basename(@hash[:filename])}"
257261
end
258262
def initialize(input, job_input)
259263
@input = input
260264
@job_input = job_input
261-
super('movie', { filename: input[:cached_file] }, input[:dimensions])
265+
# filename: @input[:cached_file]
266+
super('movie', {}, @input[:dimensions])
267+
end
268+
def filter_command(*)
269+
Filter.__outsize['w'], Filter.__outsize['h'] = @dimensions.split('x')
270+
index = FilterSourceRaw.input_index
271+
FilterSourceRaw.input_index += 1
272+
"[#{index}:v]"
262273
end
263274
end
275+
# video source
276+
class FilterSourceVideo < FilterSourceRaw
277+
def inputs
278+
[{ i: @input[:cached_file] }]
279+
end
280+
end
281+
# image source
282+
class FilterSourceImage < FilterSourceRaw
283+
def inputs
284+
[{ loop: 1, i: @input[:cached_file] }]
285+
end
286+
end
287+
264288
# simple color source
265289
class FilterSourceColor < FilterSource
266290
def filter_command(scope)

lib/graphs/layers.rb

+8-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def layer_scope(scope)
3232
# base class for a video or image layer
3333
class LayerRaw < Layer # LayerRawVideo, LayerRawImage
3434
def inputs
35-
[{ i: @input[:cached_file] }]
35+
@filter_movie.inputs
3636
end
3737
def layer_command(scope)
3838
scope[:mm_input_dimensions] = @input[:dimensions]
@@ -47,15 +47,13 @@ def layer_command(scope)
4747
class LayerRawImage < LayerRaw
4848
def initialize_chains
4949
chain = Chain.new(nil, @job_input)
50-
chain << FilterSourceMovie.new(@input, @job_input)
50+
@filter_movie = FilterSourceImage.new(@input, @job_input)
51+
chain << @filter_movie
5152
@filter_timestamps = FilterSetpts.new
5253
chain << @filter_timestamps
5354
@chains << chain
5455
super
5556
end
56-
def inputs
57-
[{ loop: 1, i: @input[:cached_file] }]
58-
end
5957
def layer_command(scope)
6058
unless @input[:cached_file]
6159
raise(Error::JobInput, "no cached_file #{@input}")
@@ -69,7 +67,8 @@ class LayerRawVideo < LayerRaw
6967
def initialize_chains
7068
# puts "LayerRawVideo#initialize_chains"
7169
chain = Chain.new(nil, @job_input)
72-
chain << FilterSourceMovie.new(@input, @job_input)
70+
@filter_movie = FilterSourceVideo.new(@input, @job_input)
71+
chain << @filter_movie
7372
# trim filter, if needed
7473
@trim_filter = __filter_trim_input
7574
chain << @trim_filter if @trim_filter
@@ -171,14 +170,14 @@ def layer_command(scope)
171170
@graphs.length.times do |i|
172171
graph = @graphs[i]
173172
layer_label = "#{layer_letter}_#{Type::TRANSITION}"
174-
cmd = graph.graph_command(scope[:mm_output])
173+
cmd = graph.graph_command(scope[:mm_output], true)
175174
layer_chain = @layer_chains[i]
176-
cmd += ','
175+
cmd += ',' unless cmd.end_with?(':v]')
177176
cmd += layer_chain[:scaler].chain_command(scope)
178177
if layer_chain[:filters]
179178
chain_cmd = layer_chain[:filters].chain_command(scope)
180179
unless chain_cmd.to_s.empty?
181-
cmd += ','
180+
cmd += ',' unless cmd.end_with?(':v]')
182181
cmd += chain_cmd
183182
end
184183
end

lib/job.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ def __execute_and_log(options)
350350
result = ShellHelper.capture(command)
351351
log_entry(:debug) { result }
352352
@hash[:results] << result
353-
logs = ShellHelper.raise_unless_rendered(result, options)
353+
logs = ShellHelper.raise_unless_rendered(result, command, options)
354354
logs.each do |hash|
355355
hash.each do |sym, proc|
356356
log_entry(sym, &proc)

lib/output.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ module MovieMasher
2121
# setting Job#error the problem is added to Job#log as a warning.
2222
#
2323
# Output.create {
24-
# type: Type::VIDEO,
25-
# name: "video.mp4", # extension implies format
26-
# dimensions: "512x288", # ffmpeg -s switch
24+
# type: Type::VIDEO, # aka 'video'
25+
# name: 'video.mp4', # implied extension
26+
# dimensions: '512x288', # ffmpeg -s switch
2727
# video_rate: 30, # ffmpeg -r:v switch
28-
# video_codec: "libx264 -preset medium", # ffmpeg -c:v switch
29-
# video_bitrate: "2000K", # ffmpeg -b:v switch
28+
# video_codec: 'libx264 -preset medium', # ffmpeg -c:v switch
29+
# video_bitrate: '2000K', # ffmpeg -b:v switch
30+
# pixel_format: 'yuv420p' # ffmpeg -pix_fmt switch
3031
# }
3132
class Output < Hashable
3233
# Returns a new instance.
@@ -48,6 +49,7 @@ def self.init_hash(output)
4849
Hashable._init_key output, :extension, 'mp4'
4950
Hashable._init_key output, :fill, Fill::NONE
5051
Hashable._init_key output, :video_rate, 30
52+
Hashable._init_key output, :pixel_format, 'yuv420p'
5153
Hashable._init_key output, :gain, Gain::None
5254
Hashable._init_key output, :precision, 1
5355
Hashable._init_key output, :video_bitrate, 2_000
@@ -61,7 +63,7 @@ def self.init_hash(output)
6163
Hashable._init_key output, :quality, 1
6264
output[:no_audio] = true
6365
when Type::IMAGE
64-
# Hashable._init_key output, :video_rate, 1
66+
Hashable._init_key output, :video_rate, 10
6567
Hashable._init_key output, :backcolor, 'black'
6668
Hashable._init_key output, :quality, 1
6769
Hashable._init_key output, :extension, 'jpg'

0 commit comments

Comments
 (0)