]> git.sesse.net Git - nageru/blob - nageru/scene.cpp
Make it possible for auto white balance to be controlled by another input.
[nageru] / nageru / scene.cpp
1 #include <assert.h>
2 extern "C" {
3 #include <lauxlib.h>
4 #include <lua.hpp>
5 }
6
7 #ifdef HAVE_CEF
8 #include "cef_capture.h"
9 #endif
10 #include "ffmpeg_capture.h"
11 #include "flags.h"
12 #include "image_input.h"
13 #include "input_state.h"
14 #include "lua_utils.h"
15 #include "scene.h"
16 #include "theme.h"
17
18 using namespace movit;
19 using namespace std;
20
21 bool display(Block *block, lua_State *L, int idx);
22
23 EffectType current_type(const Block *block)
24 {
25         return block->alternatives[block->currently_chosen_alternative]->effect_type;
26 }
27
28 int find_index_of(const Block *block, EffectType val)
29 {
30         for (size_t idx = 0; idx < block->alternatives.size(); ++idx) {
31                 if (block->alternatives[idx]->effect_type == val) {
32                         return idx;
33                 }
34         }
35         return -1;
36 }
37
38 string get_declaration_point(lua_State *L)
39 {
40         lua_Debug ar;
41         lua_getstack(L, 1, &ar);
42         lua_getinfo(L, "nSl", &ar);
43         char buf[256];
44         snprintf(buf, sizeof(buf), "%s:%d", ar.source, ar.currentline);
45         return buf;
46 }
47
48 Scene::Scene(Theme *theme, float aspect_nom, float aspect_denom)
49         : theme(theme), aspect_nom(aspect_nom), aspect_denom(aspect_denom), resource_pool(theme->get_resource_pool()) {}
50
51 size_t Scene::compute_chain_number(bool is_main_chain) const
52 {
53         assert(chains.size() > 0);
54         assert(chains.size() % 2 == 0);
55         bitset<256> disabled = find_disabled_blocks(size_t(-1));
56
57         size_t chain_number = compute_chain_number_for_block(blocks.size() - 1, disabled);
58         assert(chain_number < chains.size() / 2);
59         if (is_main_chain) {
60                 chain_number += chains.size() / 2;
61         }
62         return chain_number;
63 }
64
65 size_t Scene::compute_chain_number_for_block(size_t block_idx, const bitset<256> &disabled) const
66 {
67         Block *block = blocks[block_idx];
68         size_t chain_number;
69
70         size_t currently_chosen_alternative;
71         if (disabled.test(block_idx)) {
72                 // It doesn't matter, so pick the canonical choice
73                 // (this is the only one that is actually instantiated).
74                 currently_chosen_alternative = block->canonical_alternative;
75         } else {
76                 currently_chosen_alternative = block->currently_chosen_alternative;
77         }
78         assert(currently_chosen_alternative < block->alternatives.size());
79
80         if (block_idx == 0) {
81                 assert(block->cardinality_base == 1);
82                 chain_number = currently_chosen_alternative;
83         } else {
84                 chain_number = compute_chain_number_for_block(block_idx - 1, disabled) + block->cardinality_base * currently_chosen_alternative;
85         }
86         return chain_number;
87 }
88
89 bitset<256> Scene::find_disabled_blocks(size_t chain_idx) const
90 {
91         assert(blocks.size() < 256);
92
93         // The find_disabled_blocks() recursion logic needs only one pass by itself,
94         // but the disabler logic is not so smart, so we just run multiple times
95         // until it converges.
96         bitset<256> prev, ret;
97         do {
98                 find_disabled_blocks(chain_idx, blocks.size() - 1, /*currently_disabled=*/false, &ret);
99                 prev = ret;
100
101                 // Propagate DISABLE_IF_OTHER_DISABLED constraints (we can always do this).
102                 for (Block *block : blocks) {
103                         if (ret.test(block->idx)) continue;  // Already disabled.
104
105                         EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
106                         if (chosen_type == IDENTITY_EFFECT) {
107                                 ret.set(block->idx);
108                                 continue;
109                         }
110
111                         for (const Block::Disabler &disabler : block->disablers) {
112                                 Block *other = blocks[disabler.block_idx];
113                                 EffectType chosen_type = other->alternatives[other->chosen_alternative(chain_idx)]->effect_type;
114                                 bool other_disabled = ret.test(disabler.block_idx) || chosen_type == IDENTITY_EFFECT;
115                                 if (other_disabled && disabler.condition == Block::Disabler::DISABLE_IF_OTHER_DISABLED) {
116                                         ret.set(block->idx);
117                                         break;
118                                 }
119                         }
120                 }
121
122                 // We cannot propagate DISABLE_IF_OTHER_ENABLED in all cases;
123                 // the problem is that if A is disabled if B is enabled,
124                 // then we cannot disable A unless we actually know for sure
125                 // that B _is_ enabled. (E.g., imagine that B is disabled
126                 // if C is enabled -- we couldn't disable A before we knew if
127                 // C was enabled or not!)
128                 //
129                 // We could probably fix a fair amount of these, but the
130                 // primary use case for DISABLE_IF_OTHER_ENABLED is really
131                 // mutual exclusion; A must be disabled if B is enabled
132                 // _and_ vice versa. These loops cannot be automatically
133                 // resolved; it would depend on what A and B is. Thus,
134                 // we simply declare this kind of constraint to be a promise
135                 // from the user, not something that we'll solve for them.
136         } while (prev != ret);
137         return ret;
138 }
139
140 void Scene::find_disabled_blocks(size_t chain_idx, size_t block_idx, bool currently_disabled, bitset<256> *disabled) const
141 {
142         if (currently_disabled) {
143                 disabled->set(block_idx);
144         }
145         Block *block = blocks[block_idx];
146         EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
147         for (size_t input_idx = 0; input_idx < block->inputs.size(); ++input_idx) {
148                 if (chosen_type == IDENTITY_EFFECT && input_idx > 0) {
149                         // Multi-input effect that has been replaced by
150                         // IdentityEffect, so every effect but the first are
151                         // disabled and will not participate in the chain.
152                         find_disabled_blocks(chain_idx, block->inputs[input_idx], /*currently_disabled=*/true, disabled);
153                 } else {
154                         // Just keep on recursing down.
155                         find_disabled_blocks(chain_idx, block->inputs[input_idx], currently_disabled, disabled);
156                 }
157         }
158 }
159
160 bool Scene::is_noncanonical_chain(size_t chain_idx) const
161 {
162         bitset<256> disabled = find_disabled_blocks(chain_idx);
163         assert(blocks.size() < 256);
164         for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
165                 Block *block = blocks[block_idx];
166                 if (disabled.test(block_idx) && block->chosen_alternative(chain_idx) != block->canonical_alternative) {
167                         return true;
168                 }
169
170                 // Test if we're supposed to be disabled by some other block being enabled;
171                 // the disabled bit mask does not fully capture this.
172                 if (!disabled.test(block_idx)) {
173                         for (const Block::Disabler &disabler : block->disablers) {
174                                 if (disabler.condition == Block::Disabler::DISABLE_IF_OTHER_ENABLED &&
175                                     !disabled.test(disabler.block_idx)) {
176                                         return true;
177                                 }
178                         }
179
180                         // Auto white balance is always disabled for image inputs.
181                         if (block->white_balance_controller_block != nullptr) {
182                                 const Block *input = block->white_balance_controller_block;
183                                 if (input->alternatives[input->chosen_alternative(chain_idx)]->effect_type == IMAGE_INPUT) {
184                                         return true;
185                                 }
186                         }
187                 }
188         }
189         return false;
190 }
191
192 int Scene::add_input(lua_State* L)
193 {
194         assert(lua_gettop(L) == 1 || lua_gettop(L) == 2);
195         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
196
197         Block *block = new Block;
198         block->declaration_point = get_declaration_point(L);
199         block->idx = scene->blocks.size();
200         if (lua_gettop(L) == 1) {
201                 // No parameter given, so a flexible input.
202                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR));
203                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR_WITH_DEINTERLACE));
204                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR_PLANAR));
205                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_BGRA));
206                 block->alternatives.emplace_back(new EffectBlueprint(IMAGE_INPUT));
207         } else {
208                 // Input of a given type. We'll specialize it here, plus connect the input as given.
209                 if (lua_isnumber(L, 2)) {
210                         block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR));
211                         block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR_WITH_DEINTERLACE));
212 #ifdef HAVE_CEF
213                 } else if (luaL_testudata(L, 2, "HTMLInput")) {
214                         block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_BGRA));
215 #endif
216                 } else if (luaL_testudata(L, 2, "VideoInput")) {
217                         FFmpegCapture *capture = *(FFmpegCapture **)luaL_checkudata(L, 2, "VideoInput");
218                         if (capture->get_current_pixel_format() == bmusb::PixelFormat_8BitYCbCrPlanar) {
219                                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_YCBCR_PLANAR));
220                         } else {
221                                 assert(capture->get_current_pixel_format() == bmusb::PixelFormat_8BitBGRA);
222                                 block->alternatives.emplace_back(new EffectBlueprint(LIVE_INPUT_BGRA));
223                         }
224                 } else if (luaL_testudata(L, 2, "ImageInput")) {
225                         block->alternatives.emplace_back(new EffectBlueprint(IMAGE_INPUT));
226                 } else {
227                         luaL_error(L, "add_input() called with something that's not a signal (a signal number, a HTML input, or a VideoInput)");
228                 }
229                 bool ok = display(block, L, 2);
230                 assert(ok);
231         }
232         block->is_input = true;
233         scene->blocks.push_back(block);
234
235         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
236 }
237
238 Block *Scene::find_block_from_arg(lua_State *L, Scene *scene, int idx)
239 {
240         if (luaL_testudata(L, idx, "Block")) {
241                 return *(Block **)luaL_checkudata(L, idx, "Block");
242         } else {
243                 EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint");
244
245                 // Search through all the blocks to figure out which one contains this effect.
246                 for (Block *block : scene->blocks) {
247                         if (find(block->alternatives.begin(), block->alternatives.end(), blueprint) != block->alternatives.end()) {
248                                 return block;
249                         }
250                 }
251                 luaL_error(L, "Input effect in parameter #%d has not been added to this scene", idx - 1);
252                 return nullptr;  // Dead code.
253         }
254 }
255
256 void Scene::find_inputs_for_block(lua_State *L, Scene *scene, Block *block, int first_input_idx)
257 {
258         if (lua_gettop(L) == first_input_idx - 1) {
259                 // Implicitly the last added effect.
260                 assert(!scene->blocks.empty());
261                 block->inputs.push_back(scene->blocks.size() - 1);
262                 return;
263         }
264
265         for (int idx = first_input_idx; idx <= lua_gettop(L); ++idx) {
266                 block->inputs.push_back(find_block_from_arg(L, scene, idx)->idx);
267         }
268 }
269
270 int Scene::add_effect(lua_State* L)
271 {
272         assert(lua_gettop(L) >= 2);
273         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
274
275         Block *block = new Block;
276         block->declaration_point = get_declaration_point(L);
277         block->idx = scene->blocks.size();
278
279         if (lua_istable(L, 2)) {
280                 size_t len = lua_objlen(L, 2);
281                 for (size_t i = 0; i < len; ++i) {
282                         lua_rawgeti(L, 2, i + 1);
283                         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, -1, "EffectBlueprint");
284                         block->alternatives.push_back(blueprint);
285                         lua_settop(L, -2);
286                 }
287         } else {
288                 EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 2, "EffectBlueprint");
289                 block->alternatives.push_back(blueprint);
290         }
291
292         int identity_index = find_index_of(block, IDENTITY_EFFECT);
293         if (identity_index == -1) {
294                 block->canonical_alternative = 0;
295         } else {
296                 // Pick the IdentityEffect as the canonical alternative, in case it
297                 // helps us disable more stuff.
298                 block->canonical_alternative = identity_index;
299         }
300
301         find_inputs_for_block(L, scene, block);
302         scene->blocks.push_back(block);
303
304         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
305 }
306
307 int Scene::add_optional_effect(lua_State* L)
308 {
309         assert(lua_gettop(L) >= 2);
310         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
311
312         Block *block = new Block;
313         block->declaration_point = get_declaration_point(L);
314         block->idx = scene->blocks.size();
315
316         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 2, "EffectBlueprint");
317         block->alternatives.push_back(blueprint);
318
319         // An IdentityEffect will be the alternative for when the effect is disabled.
320         block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT));
321
322         block->canonical_alternative = 1;
323
324         find_inputs_for_block(L, scene, block);
325         scene->blocks.push_back(block);
326
327         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
328 }
329
330 const Block *Scene::find_root_input_block(lua_State *L, const Block *block)
331 {
332         if (block->is_input) {
333                 assert(block->inputs.size() == 0);
334                 return block;
335         }
336
337         const Block *ret = nullptr;
338         for (size_t input_idx : block->inputs) {
339                 const Block *parent = find_root_input_block(L, blocks[input_idx]);
340                 if (parent != nullptr) {
341                         if (ret != nullptr) {
342                                 luaL_error(L, "add_auto_white_balance() was connected to more than one input");
343                         }
344                         ret = parent;
345                 }
346         }
347         return ret;
348 }
349
350 int Scene::add_auto_white_balance(lua_State* L)
351 {
352         assert(lua_gettop(L) >= 1);
353         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
354
355         Block *block = new Block;
356         block->declaration_point = get_declaration_point(L);
357         block->idx = scene->blocks.size();
358
359         block->alternatives.push_back(new EffectBlueprint(WHITE_BALANCE_EFFECT));
360         block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT));
361
362         block->canonical_alternative = 1;
363
364         if (lua_gettop(L) == 1) {
365                 // The last added effect is implicitly both the input and gives the white balance controller.
366                 assert(!scene->blocks.empty());
367                 block->inputs.push_back(scene->blocks.size() - 1);
368                 block->white_balance_controller_block = scene->find_root_input_block(L, block);
369         } else if (lua_gettop(L) == 2) {
370                 // The given effect is both the input and the white balance controller.
371                 block->inputs.push_back(find_block_from_arg(L, scene, 2)->idx);
372                 block->white_balance_controller_block = scene->find_root_input_block(L, block);
373         } else if (lua_gettop(L) == 3) {
374                 // We have explicit input and white balance controller.
375                 block->inputs.push_back(find_block_from_arg(L, scene, 2)->idx);
376                 block->white_balance_controller_block = find_block_from_arg(L, scene, 3);
377         } else {
378                 luaL_error(L, "add_auto_white_balance([input], [white_balance_controller]) takes zero, one or two arguments");
379         }
380         if (block->white_balance_controller_block == nullptr || !block->white_balance_controller_block->is_input) {
381                 luaL_error(L, "add_auto_white_balance() does not get its white balance from an input");
382         }
383
384         scene->blocks.push_back(block);
385
386         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
387 }
388
389 Effect *Scene::instantiate_effects(const Block *block, size_t chain_idx, Scene::Instantiation *instantiation)
390 {
391         // Find the chosen alternative for this block in this instance.
392         EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
393
394         vector<Effect *> inputs;
395         for (size_t input_idx : block->inputs) {
396                 inputs.push_back(instantiate_effects(blocks[input_idx], chain_idx, instantiation));
397
398                 // As a special case, we allow IdentityEffect to take only one input
399                 // even if the other alternative (or alternatives) is multi-input.
400                 // Thus, even if there are more than one inputs, instantiate only
401                 // the first one.
402                 if (chosen_type == IDENTITY_EFFECT) {
403                         break;
404                 }
405         }
406
407         Effect *effect;
408         switch (chosen_type) {
409         case LIVE_INPUT_YCBCR:
410         case LIVE_INPUT_YCBCR_WITH_DEINTERLACE:
411         case LIVE_INPUT_YCBCR_PLANAR:
412         case LIVE_INPUT_BGRA: {
413                 bool deinterlace = (chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
414                 bool override_bounce = !deinterlace;  // For most chains, this will be fine. Reconsider if we see real problems somewhere; it's better than having the user try to understand it.
415                 bmusb::PixelFormat pixel_format;
416                 if (chosen_type == LIVE_INPUT_BGRA) {
417                         pixel_format = bmusb::PixelFormat_8BitBGRA;
418                 } else if (chosen_type == LIVE_INPUT_YCBCR_PLANAR) {
419                         pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar;
420                 } else if (global_flags.ten_bit_input) {
421                         pixel_format = bmusb::PixelFormat_10BitYCbCr;
422                 } else {
423                         pixel_format = bmusb::PixelFormat_8BitYCbCr;
424                 }
425                 LiveInputWrapper *input = new LiveInputWrapper(theme, instantiation->chain.get(), pixel_format, override_bounce, deinterlace, /*user_connectable=*/true);
426                 effect = input->get_effect();  // Adds itself to the chain, so no need to call add_effect().
427                 instantiation->inputs.emplace(block->idx, input);
428                 break;
429         }
430         case IMAGE_INPUT: {
431                 ImageInput *input = new ImageInput;
432                 instantiation->chain->add_input(input);
433                 instantiation->image_inputs.emplace(block->idx, input);
434                 effect = input;
435                 break;
436         }
437         default:
438                 effect = instantiate_effect(instantiation->chain.get(), chosen_type);
439                 instantiation->chain->add_effect(effect, inputs);
440                 break;
441         }
442         instantiation->effects.emplace(block->idx, effect);
443         return effect;
444 }
445
446 int Scene::finalize(lua_State* L)
447 {
448         bool only_one_mode = false;
449         bool chosen_mode = false;
450         if (lua_gettop(L) == 2) {
451                 only_one_mode = true;
452                 chosen_mode = checkbool(L, 2);
453         } else {
454                 assert(lua_gettop(L) == 1);
455         }
456         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
457         Theme *theme = get_theme_updata(L);
458
459         size_t base = 1;
460         for (Block *block : scene->blocks) {
461                 block->cardinality_base = base;
462                 base *= block->alternatives.size();
463         }
464
465         const size_t cardinality = base;
466         size_t real_cardinality = 0;
467         for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
468                 if (!scene->is_noncanonical_chain(chain_idx)) {
469                         ++real_cardinality;
470                 }
471         }
472         const size_t total_cardinality = real_cardinality * (only_one_mode ? 1 : 2);
473         if (total_cardinality > 200) {
474                 print_warning(L, "The given Scene will instantiate %zu different versions. This will take a lot of time and RAM to compile; see if you could limit some options by e.g. locking the input type in some cases (by giving a fixed input to add_input()).\n",
475                         total_cardinality);
476         }
477
478         Block *output_block = scene->blocks.back();
479         for (bool is_main_chain : { false, true }) {
480                 for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
481                         if ((only_one_mode && is_main_chain != chosen_mode) ||
482                             scene->is_noncanonical_chain(chain_idx)) {
483                                 scene->chains.emplace_back();
484                                 continue;
485                         }
486
487                         Scene::Instantiation instantiation;
488                         instantiation.chain.reset(new EffectChain(scene->aspect_nom, scene->aspect_denom, theme->get_resource_pool()));
489                         scene->instantiate_effects(output_block, chain_idx, &instantiation);
490
491                         add_outputs_and_finalize(instantiation.chain.get(), is_main_chain);
492                         scene->chains.emplace_back(move(instantiation));
493                 }
494         }
495         return 0;
496 }
497
498 int find_signal_to_connect(lua_State *L, const Block *block)
499 {
500         if (block->signal_type_to_connect == Block::CONNECT_SIGNAL) {
501                 return block->signal_to_connect;
502 #ifdef HAVE_CEF
503         } else if (block->signal_type_to_connect == Block::CONNECT_CEF) {
504                 return block->cef_to_connect->get_card_index();
505 #endif
506         } else if (block->signal_type_to_connect == Block::CONNECT_VIDEO) {
507                 return block->video_to_connect->get_card_index();
508         } else if (block->signal_type_to_connect == Block::CONNECT_NONE) {
509                 luaL_error(L, "An input in a scene was not connected to anything (forgot to call display())");
510         } else {
511                 assert(false);
512         }
513         return -1;
514 }
515
516 std::pair<movit::EffectChain *, std::function<void()>>
517 Scene::get_chain(Theme *theme, lua_State *L, unsigned num, const InputState &input_state)
518 {
519         // For video inputs, pick the right interlaced/progressive version
520         // based on the current state of the signals.
521         InputStateInfo info(input_state);
522         for (Block *block : blocks) {
523                 if (block->is_input && block->signal_type_to_connect == Block::CONNECT_SIGNAL) {
524                         EffectType chosen_type = current_type(block);
525                         assert(chosen_type == LIVE_INPUT_YCBCR || chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
526                         if (info.last_interlaced[block->signal_to_connect]) {
527                                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
528                         } else {
529                                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR);
530                         }
531                 }
532         }
533
534         // Find all auto white balance blocks, turn on and off the effect as needed,
535         // and fetch the actual white balance set (it is stored in Theme).
536         map<Block *, array<float, 3>> white_balance;
537         for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
538                 Block *block = blocks[block_idx];
539                 const Block *input = block->white_balance_controller_block;
540                 if (input == nullptr) {
541                         continue;  // Not an auto white balance block.
542                 }
543
544                 EffectType chosen_type = current_type(input);
545                 if (chosen_type == IMAGE_INPUT) {
546                         // Image inputs never get white balance applied.
547                         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
548                         continue;
549                 }
550
551                 assert(chosen_type == LIVE_INPUT_YCBCR ||
552                        chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE ||
553                        chosen_type == LIVE_INPUT_YCBCR_PLANAR ||
554                        chosen_type == LIVE_INPUT_BGRA);
555                 int signal = find_signal_to_connect(L, input);
556                 RGBTriplet wb = theme->get_white_balance_for_signal(signal);
557                 if (fabs(wb.r - 1.0) < 1e-3 && fabs(wb.g - 1.0) < 1e-3 && fabs(wb.b - 1.0) < 1e-3) {
558                         // Neutral white balance.
559                         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
560                 } else {
561                         block->currently_chosen_alternative = find_index_of(block, WHITE_BALANCE_EFFECT);
562                         white_balance.emplace(block, array<float, 3>{ wb.r, wb.g, wb.b });
563                 }
564         }
565
566         // Pick out the right chain based on the current selections,
567         // and snapshot all the set variables so that we can set them
568         // in the prepare function even if they're being changed by
569         // the Lua code later.
570         bool is_main_chain = (num == 0);
571         size_t chain_idx = compute_chain_number(is_main_chain);
572         if (is_noncanonical_chain(chain_idx)) {
573                 // This should be due to promise_to_disable_if_enabled(). Find out what
574                 // happened, to give the user some help.
575                 bitset<256> disabled = find_disabled_blocks(chain_idx);
576                 for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
577                         Block *block = blocks[block_idx];
578                         if (disabled.test(block_idx)) continue;
579                         for (const Block::Disabler &disabler : block->disablers) {
580                                 if (disabler.condition == Block::Disabler::DISABLE_IF_OTHER_ENABLED &&
581                                     !disabled.test(disabler.block_idx)) {
582                                         fprintf(stderr, "Promise declared at %s violated.\n", disabler.declaration_point.c_str());
583                                         abort();
584                                 }
585                         }
586                 }
587                 assert(false);  // Something else happened, seemingly.
588         }
589         const Scene::Instantiation &instantiation = chains[chain_idx];
590         EffectChain *effect_chain = instantiation.chain.get();
591
592         map<LiveInputWrapper *, int> signals_to_connect;
593         map<ImageInput *, string> images_to_select;
594         map<pair<Effect *, string>, int> int_to_set;
595         map<pair<Effect *, string>, float> float_to_set;
596         map<pair<Effect *, string>, array<float, 3>> vec3_to_set;
597         map<pair<Effect *, string>, array<float, 4>> vec4_to_set;
598         for (const auto &index_and_input : instantiation.inputs) {
599                 Block *block = blocks[index_and_input.first];
600                 EffectType chosen_type = current_type(block);
601                 if (chosen_type == LIVE_INPUT_YCBCR ||
602                     chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE ||
603                     chosen_type == LIVE_INPUT_YCBCR_PLANAR ||
604                     chosen_type == LIVE_INPUT_BGRA) {
605                         LiveInputWrapper *input = index_and_input.second;
606                         signals_to_connect.emplace(input, find_signal_to_connect(L, block));
607                 }
608         }
609         for (const auto &index_and_input : instantiation.image_inputs) {
610                 Block *block = blocks[index_and_input.first];
611                 ImageInput *input = index_and_input.second;
612                 if (current_type(block) == IMAGE_INPUT) {
613                         images_to_select.emplace(input, block->pathname);
614                 }
615         }
616         for (const auto &index_and_effect : instantiation.effects) {
617                 Block *block = blocks[index_and_effect.first];
618                 Effect *effect = index_and_effect.second;
619
620                 bool missing_width = (current_type(block) == RESIZE_EFFECT ||
621                         current_type(block) == RESAMPLE_EFFECT ||
622                         current_type(block) == PADDING_EFFECT);
623                 bool missing_height = missing_width;
624
625                 // Get the effects currently set on the block.
626                 if (current_type(block) != IDENTITY_EFFECT) {  // Ignore settings on optional effects.
627                         if (block->int_parameters.count("width") && block->int_parameters["width"] > 0) {
628                                 missing_width = false;
629                         }
630                         if (block->int_parameters.count("height") && block->int_parameters["height"] > 0) {
631                                 missing_height = false;
632                         }
633                         for (const auto &key_and_tuple : block->int_parameters) {
634                                 int_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
635                         }
636                         for (const auto &key_and_tuple : block->float_parameters) {
637                                 float_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
638                         }
639                         if (white_balance.count(block)) {
640                                 vec3_to_set.emplace(make_pair(effect, "neutral_color"), white_balance[block]);
641                         }
642                         for (const auto &key_and_tuple : block->vec3_parameters) {
643                                 vec3_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
644                         }
645                         for (const auto &key_and_tuple : block->vec4_parameters) {
646                                 vec4_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
647                         }
648                 }
649
650                 // Parameters set on the blueprint itself override those that are set for the block,
651                 // so they are set afterwards.
652                 if (!block->alternatives.empty()) {
653                         EffectBlueprint *blueprint = block->alternatives[block->currently_chosen_alternative];
654                         if (blueprint->int_parameters.count("width") && blueprint->int_parameters["width"] > 0) {
655                                 missing_width = false;
656                         }
657                         if (blueprint->int_parameters.count("height") && blueprint->int_parameters["height"] > 0) {
658                                 missing_height = false;
659                         }
660                         for (const auto &key_and_tuple : blueprint->int_parameters) {
661                                 int_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
662                         }
663                         for (const auto &key_and_tuple : blueprint->float_parameters) {
664                                 float_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
665                         }
666                         for (const auto &key_and_tuple : blueprint->vec3_parameters) {
667                                 vec3_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
668                         }
669                         for (const auto &key_and_tuple : blueprint->vec4_parameters) {
670                                 vec4_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
671                         }
672                 }
673
674                 if (missing_width || missing_height) {
675                         fprintf(stderr, "WARNING: Unset or nonpositive width/height for effect declared at %s "
676                                 "when getting scene for signal %u; setting to 1x1 to avoid crash.\n",
677                                 block->declaration_point.c_str(), num);
678                         int_to_set[make_pair(effect, "width")] = 1;
679                         int_to_set[make_pair(effect, "height")] = 1;
680                 }
681         }
682
683         lua_pop(L, 1);
684
685         auto setup_chain = [L, theme, signals_to_connect, images_to_select, int_to_set, float_to_set, vec3_to_set, vec4_to_set, input_state]{
686                 lock_guard<mutex> lock(theme->m);
687
688                 // Set up state, including connecting signals.
689                 for (const auto &input_and_signal : signals_to_connect) {
690                         LiveInputWrapper *input = input_and_signal.first;
691                         input->connect_signal_raw(input_and_signal.second, input_state);
692                 }
693                 for (const auto &input_and_filename : images_to_select) {
694                         input_and_filename.first->switch_image(input_and_filename.second);
695                 }
696                 for (const auto &effect_and_key_and_value : int_to_set) {
697                         Effect *effect = effect_and_key_and_value.first.first;
698                         const string &key = effect_and_key_and_value.first.second;
699                         const int value = effect_and_key_and_value.second;
700                         if (!effect->set_int(key, value)) {
701                                 luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), value);
702                         }
703                 }
704                 for (const auto &effect_and_key_and_value : float_to_set) {
705                         Effect *effect = effect_and_key_and_value.first.first;
706                         const string &key = effect_and_key_and_value.first.second;
707                         const float value = effect_and_key_and_value.second;
708                         if (!effect->set_float(key, value)) {
709                                 luaL_error(L, "Effect refused set_float(\"%s\", %f) (invalid key?)", key.c_str(), value);
710                         }
711                 }
712                 for (const auto &effect_and_key_and_value : vec3_to_set) {
713                         Effect *effect = effect_and_key_and_value.first.first;
714                         const string &key = effect_and_key_and_value.first.second;
715                         const float *value = effect_and_key_and_value.second.data();
716                         if (!effect->set_vec3(key, value)) {
717                                 luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(),
718                                                 value[0], value[1], value[2]);
719                         }
720                 }
721                 for (const auto &effect_and_key_and_value : vec4_to_set) {
722                         Effect *effect = effect_and_key_and_value.first.first;
723                         const string &key = effect_and_key_and_value.first.second;
724                         const float *value = effect_and_key_and_value.second.data();
725                         if (!effect->set_vec4(key, value)) {
726                                 luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(),
727                                                 value[0], value[1], value[2], value[3]);
728                         }
729                 }
730         };
731         return make_pair(effect_chain, move(setup_chain));
732 }
733
734 bool display(Block *block, lua_State *L, int idx)
735 {
736         if (lua_isnumber(L, idx)) {
737                 Theme *theme = get_theme_updata(L);
738                 int signal_idx = luaL_checknumber(L, idx);
739                 block->signal_type_to_connect = Block::CONNECT_SIGNAL;
740                 block->signal_to_connect = theme->map_signal(signal_idx);
741                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR);  // Will be changed to deinterlaced at get_chain() time if needed.
742                 return true;
743 #ifdef HAVE_CEF
744         } else if (luaL_testudata(L, idx, "HTMLInput")) {
745                 CEFCapture *capture = *(CEFCapture **)luaL_checkudata(L, idx, "HTMLInput");
746                 block->signal_type_to_connect = Block::CONNECT_CEF;
747                 block->cef_to_connect = capture;
748                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_BGRA);
749                 assert(capture->get_current_pixel_format() == bmusb::PixelFormat_8BitBGRA);
750                 return true;
751 #endif
752         } else if (luaL_testudata(L, idx, "VideoInput")) {
753                 FFmpegCapture *capture = *(FFmpegCapture **)luaL_checkudata(L, idx, "VideoInput");
754                 block->signal_type_to_connect = Block::CONNECT_VIDEO;
755                 block->video_to_connect = capture;
756                 if (capture->get_current_pixel_format() == bmusb::PixelFormat_8BitYCbCrPlanar) {
757                         block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR_PLANAR);
758                 } else {
759                         assert(capture->get_current_pixel_format() == bmusb::PixelFormat_8BitBGRA);
760                         block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_BGRA);
761                 }
762                 return true;
763         } else if (luaL_testudata(L, idx, "ImageInput")) {
764                 ImageInput *image = *(ImageInput **)luaL_checkudata(L, idx, "ImageInput");
765                 block->signal_type_to_connect = Block::CONNECT_NONE;
766                 block->currently_chosen_alternative = find_index_of(block, IMAGE_INPUT);
767                 block->pathname = image->get_pathname();
768                 return true;
769         } else {
770                 return false;
771         }
772 }
773
774 int Block_display(lua_State* L)
775 {
776         assert(lua_gettop(L) == 2);
777         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
778         if (!block->is_input) {
779                 luaL_error(L, "display() called on something that isn't an input");
780         }
781
782         bool ok = display(block, L, 2);
783         if (!ok) {
784                 luaL_error(L, "display() called with something that's not a signal (a signal number, a HTML input, or a VideoInput)");
785         }
786
787         if (block->currently_chosen_alternative == -1) {
788                 luaL_error(L, "display() called on an input whose type was fixed at construction time, with a signal of different type");
789         }
790
791         return 0;
792 }
793
794 int Block_choose(lua_State* L)
795 {
796         assert(lua_gettop(L) == 2);
797         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
798         int alternative_idx = -1;
799         if (lua_isnumber(L, 2)) {
800                 alternative_idx = luaL_checknumber(L, 2);
801         } else if (lua_istable(L, 2)) {
802                 // See if it's an Effect metatable (e.g. foo:choose(ResampleEffect))
803                 lua_getfield(L, 2, "__effect_type_id");
804                 if (lua_isnumber(L, -1)) {
805                         EffectType effect_type = EffectType(luaL_checknumber(L, -1));
806                         alternative_idx = find_index_of(block, effect_type);
807                 }
808                 lua_pop(L, 1);
809         }
810
811         if (alternative_idx == -1) {
812                 luaL_error(L, "choose() called with something that was not an index or an effect type (e.g. ResampleEffect) that was part of the alternatives");
813         }
814
815         assert(alternative_idx >= 0);
816         assert(size_t(alternative_idx) < block->alternatives.size());
817         block->currently_chosen_alternative = alternative_idx;
818
819         return wrap_lua_existing_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", block->alternatives[alternative_idx]);
820 }
821
822 int Block_enable(lua_State *L)
823 {
824         assert(lua_gettop(L) == 1);
825         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
826
827         if (block->alternatives.size() != 2 ||
828             block->alternatives[1]->effect_type != IDENTITY_EFFECT) {
829                 luaL_error(L, "enable() called on something that wasn't added with add_optional_effect()");
830         }
831         block->currently_chosen_alternative = 0;  // The actual effect.
832         return 0;
833 }
834
835 int Block_enable_if(lua_State *L)
836 {
837         assert(lua_gettop(L) == 2);
838         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
839
840         if (block->alternatives.size() != 2 ||
841             block->alternatives[1]->effect_type != IDENTITY_EFFECT) {
842                 luaL_error(L, "enable_if() called on something that wasn't added with add_optional_effect()");
843         }
844         bool enabled = checkbool(L, 2);
845         block->currently_chosen_alternative = enabled ? 0 : 1;
846         return 0;
847 }
848
849 int Block_disable(lua_State *L)
850 {
851         assert(lua_gettop(L) == 1);
852         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
853
854         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
855         if (block->currently_chosen_alternative == -1) {
856                 luaL_error(L, "disable() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
857         }
858         assert(block->currently_chosen_alternative != -1);
859         return 0;
860 }
861
862 int Block_always_disable_if_disabled(lua_State *L)
863 {
864         assert(lua_gettop(L) == 2);
865         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
866         Block *disabler_block = *(Block **)luaL_checkudata(L, 2, "Block");
867
868         int my_alternative = find_index_of(block, IDENTITY_EFFECT);
869         int their_alternative = find_index_of(disabler_block, IDENTITY_EFFECT);
870         if (my_alternative == -1) {
871                 luaL_error(L, "always_disable_if_disabled() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
872         }
873         if (their_alternative == -1) {
874                 luaL_error(L, "always_disable_if_disabled() with an argument that didn't have an IdentityEffect fallback (try add_optional_effect())");
875         }
876
877         // The declaration point isn't actually used, but it's nice for completeness.
878         block->disablers.push_back(Block::Disabler{ disabler_block->idx, Block::Disabler::DISABLE_IF_OTHER_DISABLED, get_declaration_point(L) });
879
880         lua_pop(L, 2);
881         return 0;
882 }
883
884 int Block_promise_to_disable_if_enabled(lua_State *L)
885 {
886         assert(lua_gettop(L) == 2);
887         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
888         Block *disabler_block = *(Block **)luaL_checkudata(L, 2, "Block");
889
890         int my_alternative = find_index_of(block, IDENTITY_EFFECT);
891         int their_alternative = find_index_of(disabler_block, IDENTITY_EFFECT);
892         if (my_alternative == -1) {
893                 luaL_error(L, "promise_to_disable_if_enabled() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
894         }
895         if (their_alternative == -1) {
896                 luaL_error(L, "promise_to_disable_if_enabled() with an argument that didn't have an IdentityEffect fallback (try add_optional_effect())");
897         }
898
899         block->disablers.push_back(Block::Disabler{ disabler_block->idx, Block::Disabler::DISABLE_IF_OTHER_ENABLED, get_declaration_point(L) });
900
901         lua_pop(L, 2);
902         return 0;
903 }
904
905 int Block_set_int(lua_State *L)
906 {
907         assert(lua_gettop(L) == 3);
908         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
909         string key = checkstdstring(L, 2);
910         float value = luaL_checknumber(L, 3);
911
912         // TODO: check validity already here, if possible?
913         block->int_parameters[key] = value;
914
915         return 0;
916 }
917
918 int Block_set_float(lua_State *L)
919 {
920         assert(lua_gettop(L) == 3);
921         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
922         string key = checkstdstring(L, 2);
923         float value = luaL_checknumber(L, 3);
924
925         // TODO: check validity already here, if possible?
926         block->float_parameters[key] = value;
927
928         return 0;
929 }
930
931 int Block_set_vec3(lua_State *L)
932 {
933         assert(lua_gettop(L) == 5);
934         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
935         string key = checkstdstring(L, 2);
936         array<float, 3> v;
937         v[0] = luaL_checknumber(L, 3);
938         v[1] = luaL_checknumber(L, 4);
939         v[2] = luaL_checknumber(L, 5);
940
941         // TODO: check validity already here, if possible?
942         block->vec3_parameters[key] = v;
943
944         return 0;
945 }
946
947 int Block_set_vec4(lua_State *L)
948 {
949         assert(lua_gettop(L) == 6);
950         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
951         string key = checkstdstring(L, 2);
952         array<float, 4> v;
953         v[0] = luaL_checknumber(L, 3);
954         v[1] = luaL_checknumber(L, 4);
955         v[2] = luaL_checknumber(L, 5);
956         v[3] = luaL_checknumber(L, 6);
957
958         // TODO: check validity already here, if possible?
959         block->vec4_parameters[key] = v;
960
961         return 0;
962 }
963