]> git.sesse.net Git - nageru/blob - nageru/scene.cpp
Support auto white balance (ie., not controlled by the theme).
[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->root_input_block != nullptr) {
182                                 const Block *input = block->root_input_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 void Scene::find_inputs_for_block(lua_State *L, Scene *scene, Block *block, int first_input_idx)
239 {
240         if (lua_gettop(L) == first_input_idx - 1) {
241                 // Implicitly the last added effect.
242                 assert(!scene->blocks.empty());
243                 block->inputs.push_back(scene->blocks.size() - 1);
244                 return;
245         }
246
247         for (int idx = first_input_idx; idx <= lua_gettop(L); ++idx) {
248                 Block *input_block = nullptr;
249                 if (luaL_testudata(L, idx, "Block")) {
250                         input_block = *(Block **)luaL_checkudata(L, idx, "Block");
251                 } else {
252                         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint");
253
254                         // Search through all the blocks to figure out which one contains this effect.
255                         for (Block *block : scene->blocks) {
256                                 if (find(block->alternatives.begin(), block->alternatives.end(), blueprint) != block->alternatives.end()) {
257                                         input_block = block;
258                                         break;
259                                 }
260                         }
261                         if (input_block == nullptr) {
262                                 luaL_error(L, "Input effect in parameter #%d has not been added to this scene", idx - 1);
263                         }
264                 }
265                 block->inputs.push_back(input_block->idx);
266         }
267 }
268
269 int Scene::add_effect(lua_State* L)
270 {
271         assert(lua_gettop(L) >= 2);
272         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
273
274         Block *block = new Block;
275         block->declaration_point = get_declaration_point(L);
276         block->idx = scene->blocks.size();
277
278         if (lua_istable(L, 2)) {
279                 size_t len = lua_objlen(L, 2);
280                 for (size_t i = 0; i < len; ++i) {
281                         lua_rawgeti(L, 2, i + 1);
282                         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, -1, "EffectBlueprint");
283                         block->alternatives.push_back(blueprint);
284                         lua_settop(L, -2);
285                 }
286         } else {
287                 EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 2, "EffectBlueprint");
288                 block->alternatives.push_back(blueprint);
289         }
290
291         int identity_index = find_index_of(block, IDENTITY_EFFECT);
292         if (identity_index == -1) {
293                 block->canonical_alternative = 0;
294         } else {
295                 // Pick the IdentityEffect as the canonical alternative, in case it
296                 // helps us disable more stuff.
297                 block->canonical_alternative = identity_index;
298         }
299
300         find_inputs_for_block(L, scene, block);
301         scene->blocks.push_back(block);
302
303         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
304 }
305
306 int Scene::add_optional_effect(lua_State* L)
307 {
308         assert(lua_gettop(L) >= 2);
309         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
310
311         Block *block = new Block;
312         block->declaration_point = get_declaration_point(L);
313         block->idx = scene->blocks.size();
314
315         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 2, "EffectBlueprint");
316         block->alternatives.push_back(blueprint);
317
318         // An IdentityEffect will be the alternative for when the effect is disabled.
319         block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT));
320
321         block->canonical_alternative = 1;
322
323         find_inputs_for_block(L, scene, block);
324         scene->blocks.push_back(block);
325
326         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
327 }
328
329 const Block *Scene::find_root_input_block(lua_State *L, const Block *block)
330 {
331         if (block->is_input) {
332                 assert(block->inputs.size() == 0);
333                 return block;
334         }
335
336         const Block *ret = nullptr;
337         for (size_t input_idx : block->inputs) {
338                 const Block *parent = find_root_input_block(L, blocks[input_idx]);
339                 if (parent != nullptr) {
340                         if (ret != nullptr) {
341                                 luaL_error(L, "add_auto_white_balance() was connected to more than one input");
342                         }
343                         ret = parent;
344                 }
345         }
346         return ret;
347 }
348
349 int Scene::add_auto_white_balance(lua_State* L)
350 {
351         assert(lua_gettop(L) >= 1);
352         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
353
354         Block *block = new Block;
355         block->declaration_point = get_declaration_point(L);
356         block->idx = scene->blocks.size();
357
358         block->alternatives.push_back(new EffectBlueprint(WHITE_BALANCE_EFFECT));
359         block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT));
360
361         block->canonical_alternative = 1;
362
363         find_inputs_for_block(L, scene, block, /*first_input_idx=*/2);
364
365         if (block->inputs.size() != 1) {
366                 luaL_error(L, "add_auto_white_balance() needs exactly one input");
367         }
368         block->root_input_block = scene->find_root_input_block(L, block);
369         if (block->root_input_block == nullptr) {
370                 luaL_error(L, "add_auto_white_balance() was not connected to an input");
371         }
372
373         scene->blocks.push_back(block);
374
375         return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
376 }
377
378 Effect *Scene::instantiate_effects(const Block *block, size_t chain_idx, Scene::Instantiation *instantiation)
379 {
380         // Find the chosen alternative for this block in this instance.
381         EffectType chosen_type = block->alternatives[block->chosen_alternative(chain_idx)]->effect_type;
382
383         vector<Effect *> inputs;
384         for (size_t input_idx : block->inputs) {
385                 inputs.push_back(instantiate_effects(blocks[input_idx], chain_idx, instantiation));
386
387                 // As a special case, we allow IdentityEffect to take only one input
388                 // even if the other alternative (or alternatives) is multi-input.
389                 // Thus, even if there are more than one inputs, instantiate only
390                 // the first one.
391                 if (chosen_type == IDENTITY_EFFECT) {
392                         break;
393                 }
394         }
395
396         Effect *effect;
397         switch (chosen_type) {
398         case LIVE_INPUT_YCBCR:
399         case LIVE_INPUT_YCBCR_WITH_DEINTERLACE:
400         case LIVE_INPUT_YCBCR_PLANAR:
401         case LIVE_INPUT_BGRA: {
402                 bool deinterlace = (chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
403                 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.
404                 bmusb::PixelFormat pixel_format;
405                 if (chosen_type == LIVE_INPUT_BGRA) {
406                         pixel_format = bmusb::PixelFormat_8BitBGRA;
407                 } else if (chosen_type == LIVE_INPUT_YCBCR_PLANAR) {
408                         pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar;
409                 } else if (global_flags.ten_bit_input) {
410                         pixel_format = bmusb::PixelFormat_10BitYCbCr;
411                 } else {
412                         pixel_format = bmusb::PixelFormat_8BitYCbCr;
413                 }
414                 LiveInputWrapper *input = new LiveInputWrapper(theme, instantiation->chain.get(), pixel_format, override_bounce, deinterlace, /*user_connectable=*/true);
415                 effect = input->get_effect();  // Adds itself to the chain, so no need to call add_effect().
416                 instantiation->inputs.emplace(block->idx, input);
417                 break;
418         }
419         case IMAGE_INPUT: {
420                 ImageInput *input = new ImageInput;
421                 instantiation->chain->add_input(input);
422                 instantiation->image_inputs.emplace(block->idx, input);
423                 effect = input;
424                 break;
425         }
426         default:
427                 effect = instantiate_effect(instantiation->chain.get(), chosen_type);
428                 instantiation->chain->add_effect(effect, inputs);
429                 break;
430         }
431         instantiation->effects.emplace(block->idx, effect);
432         return effect;
433 }
434
435 int Scene::finalize(lua_State* L)
436 {
437         bool only_one_mode = false;
438         bool chosen_mode = false;
439         if (lua_gettop(L) == 2) {
440                 only_one_mode = true;
441                 chosen_mode = checkbool(L, 2);
442         } else {
443                 assert(lua_gettop(L) == 1);
444         }
445         Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
446         Theme *theme = get_theme_updata(L);
447
448         size_t base = 1;
449         for (Block *block : scene->blocks) {
450                 block->cardinality_base = base;
451                 base *= block->alternatives.size();
452         }
453
454         const size_t cardinality = base;
455         size_t real_cardinality = 0;
456         for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
457                 if (!scene->is_noncanonical_chain(chain_idx)) {
458                         ++real_cardinality;
459                 }
460         }
461         const size_t total_cardinality = real_cardinality * (only_one_mode ? 1 : 2);
462         if (total_cardinality > 200) {
463                 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",
464                         total_cardinality);
465         }
466
467         Block *output_block = scene->blocks.back();
468         for (bool is_main_chain : { false, true }) {
469                 for (size_t chain_idx = 0; chain_idx < cardinality; ++chain_idx) {
470                         if ((only_one_mode && is_main_chain != chosen_mode) ||
471                             scene->is_noncanonical_chain(chain_idx)) {
472                                 scene->chains.emplace_back();
473                                 continue;
474                         }
475
476                         Scene::Instantiation instantiation;
477                         instantiation.chain.reset(new EffectChain(scene->aspect_nom, scene->aspect_denom, theme->get_resource_pool()));
478                         scene->instantiate_effects(output_block, chain_idx, &instantiation);
479
480                         add_outputs_and_finalize(instantiation.chain.get(), is_main_chain);
481                         scene->chains.emplace_back(move(instantiation));
482                 }
483         }
484         return 0;
485 }
486
487 int find_signal_to_connect(lua_State *L, const Block *block)
488 {
489         if (block->signal_type_to_connect == Block::CONNECT_SIGNAL) {
490                 return block->signal_to_connect;
491 #ifdef HAVE_CEF
492         } else if (block->signal_type_to_connect == Block::CONNECT_CEF) {
493                 return block->cef_to_connect->get_card_index();
494 #endif
495         } else if (block->signal_type_to_connect == Block::CONNECT_VIDEO) {
496                 return block->video_to_connect->get_card_index();
497         } else if (block->signal_type_to_connect == Block::CONNECT_NONE) {
498                 luaL_error(L, "An input in a scene was not connected to anything (forgot to call display())");
499         } else {
500                 assert(false);
501         }
502         return -1;
503 }
504
505 std::pair<movit::EffectChain *, std::function<void()>>
506 Scene::get_chain(Theme *theme, lua_State *L, unsigned num, const InputState &input_state)
507 {
508         // For video inputs, pick the right interlaced/progressive version
509         // based on the current state of the signals.
510         InputStateInfo info(input_state);
511         for (Block *block : blocks) {
512                 if (block->is_input && block->signal_type_to_connect == Block::CONNECT_SIGNAL) {
513                         EffectType chosen_type = current_type(block);
514                         assert(chosen_type == LIVE_INPUT_YCBCR || chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
515                         if (info.last_interlaced[block->signal_to_connect]) {
516                                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR_WITH_DEINTERLACE);
517                         } else {
518                                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR);
519                         }
520                 }
521         }
522
523         // Find all auto white balance blocks, turn on and off the effect as needed,
524         // and fetch the actual white balance set (it is stored in Theme).
525         map<Block *, array<float, 3>> white_balance;
526         for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
527                 Block *block = blocks[block_idx];
528                 const Block *input = block->root_input_block;
529                 if (input == nullptr) {
530                         continue;  // Not an auto white balance block.
531                 }
532
533                 EffectType chosen_type = current_type(input);
534                 if (chosen_type == IMAGE_INPUT) {
535                         // Image inputs never get white balance applied.
536                         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
537                         continue;
538                 }
539
540                 assert(chosen_type == LIVE_INPUT_YCBCR ||
541                        chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE ||
542                        chosen_type == LIVE_INPUT_YCBCR_PLANAR ||
543                        chosen_type == LIVE_INPUT_BGRA);
544                 int signal = find_signal_to_connect(L, input);
545                 Theme::WhiteBalance wb = theme->get_white_balance_for_signal(signal);
546                 if (fabs(wb.r - 1.0) < 1e-3 && fabs(wb.g - 1.0) < 1e-3 && fabs(wb.b - 1.0) < 1e-3) {
547                         // Neutral white balance.
548                         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
549                 } else {
550                         block->currently_chosen_alternative = find_index_of(block, WHITE_BALANCE_EFFECT);
551                         white_balance.emplace(block, array<float, 3>{ wb.r, wb.g, wb.b });
552                 }
553         }
554
555         // Pick out the right chain based on the current selections,
556         // and snapshot all the set variables so that we can set them
557         // in the prepare function even if they're being changed by
558         // the Lua code later.
559         bool is_main_chain = (num == 0);
560         size_t chain_idx = compute_chain_number(is_main_chain);
561         if (is_noncanonical_chain(chain_idx)) {
562                 // This should be due to promise_to_disable_if_enabled(). Find out what
563                 // happened, to give the user some help.
564                 bitset<256> disabled = find_disabled_blocks(chain_idx);
565                 for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
566                         Block *block = blocks[block_idx];
567                         if (disabled.test(block_idx)) continue;
568                         for (const Block::Disabler &disabler : block->disablers) {
569                                 if (disabler.condition == Block::Disabler::DISABLE_IF_OTHER_ENABLED &&
570                                     !disabled.test(disabler.block_idx)) {
571                                         fprintf(stderr, "Promise declared at %s violated.\n", disabler.declaration_point.c_str());
572                                         abort();
573                                 }
574                         }
575                 }
576                 assert(false);  // Something else happened, seemingly.
577         }
578         const Scene::Instantiation &instantiation = chains[chain_idx];
579         EffectChain *effect_chain = instantiation.chain.get();
580
581         map<LiveInputWrapper *, int> signals_to_connect;
582         map<ImageInput *, string> images_to_select;
583         map<pair<Effect *, string>, int> int_to_set;
584         map<pair<Effect *, string>, float> float_to_set;
585         map<pair<Effect *, string>, array<float, 3>> vec3_to_set;
586         map<pair<Effect *, string>, array<float, 4>> vec4_to_set;
587         for (const auto &index_and_input : instantiation.inputs) {
588                 Block *block = blocks[index_and_input.first];
589                 EffectType chosen_type = current_type(block);
590                 if (chosen_type == LIVE_INPUT_YCBCR ||
591                     chosen_type == LIVE_INPUT_YCBCR_WITH_DEINTERLACE ||
592                     chosen_type == LIVE_INPUT_YCBCR_PLANAR ||
593                     chosen_type == LIVE_INPUT_BGRA) {
594                         LiveInputWrapper *input = index_and_input.second;
595                         signals_to_connect.emplace(input, find_signal_to_connect(L, block));
596                 }
597         }
598         for (const auto &index_and_input : instantiation.image_inputs) {
599                 Block *block = blocks[index_and_input.first];
600                 ImageInput *input = index_and_input.second;
601                 if (current_type(block) == IMAGE_INPUT) {
602                         images_to_select.emplace(input, block->pathname);
603                 }
604         }
605         for (const auto &index_and_effect : instantiation.effects) {
606                 Block *block = blocks[index_and_effect.first];
607                 Effect *effect = index_and_effect.second;
608
609                 bool missing_width = (current_type(block) == RESIZE_EFFECT ||
610                         current_type(block) == RESAMPLE_EFFECT ||
611                         current_type(block) == PADDING_EFFECT);
612                 bool missing_height = missing_width;
613
614                 // Get the effects currently set on the block.
615                 if (current_type(block) != IDENTITY_EFFECT) {  // Ignore settings on optional effects.
616                         if (block->int_parameters.count("width") && block->int_parameters["width"] > 0) {
617                                 missing_width = false;
618                         }
619                         if (block->int_parameters.count("height") && block->int_parameters["height"] > 0) {
620                                 missing_height = false;
621                         }
622                         for (const auto &key_and_tuple : block->int_parameters) {
623                                 int_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
624                         }
625                         for (const auto &key_and_tuple : block->float_parameters) {
626                                 float_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
627                         }
628                         if (white_balance.count(block)) {
629                                 vec3_to_set.emplace(make_pair(effect, "neutral_color"), white_balance[block]);
630                         }
631                         for (const auto &key_and_tuple : block->vec3_parameters) {
632                                 vec3_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
633                         }
634                         for (const auto &key_and_tuple : block->vec4_parameters) {
635                                 vec4_to_set.emplace(make_pair(effect, key_and_tuple.first), key_and_tuple.second);
636                         }
637                 }
638
639                 // Parameters set on the blueprint itself override those that are set for the block,
640                 // so they are set afterwards.
641                 if (!block->alternatives.empty()) {
642                         EffectBlueprint *blueprint = block->alternatives[block->currently_chosen_alternative];
643                         if (blueprint->int_parameters.count("width") && blueprint->int_parameters["width"] > 0) {
644                                 missing_width = false;
645                         }
646                         if (blueprint->int_parameters.count("height") && blueprint->int_parameters["height"] > 0) {
647                                 missing_height = false;
648                         }
649                         for (const auto &key_and_tuple : blueprint->int_parameters) {
650                                 int_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
651                         }
652                         for (const auto &key_and_tuple : blueprint->float_parameters) {
653                                 float_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
654                         }
655                         for (const auto &key_and_tuple : blueprint->vec3_parameters) {
656                                 vec3_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
657                         }
658                         for (const auto &key_and_tuple : blueprint->vec4_parameters) {
659                                 vec4_to_set[make_pair(effect, key_and_tuple.first)] = key_and_tuple.second;
660                         }
661                 }
662
663                 if (missing_width || missing_height) {
664                         fprintf(stderr, "WARNING: Unset or nonpositive width/height for effect declared at %s "
665                                 "when getting scene for signal %u; setting to 1x1 to avoid crash.\n",
666                                 block->declaration_point.c_str(), num);
667                         int_to_set[make_pair(effect, "width")] = 1;
668                         int_to_set[make_pair(effect, "height")] = 1;
669                 }
670         }
671
672         lua_pop(L, 1);
673
674         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]{
675                 lock_guard<mutex> lock(theme->m);
676
677                 // Set up state, including connecting signals.
678                 for (const auto &input_and_signal : signals_to_connect) {
679                         LiveInputWrapper *input = input_and_signal.first;
680                         input->connect_signal_raw(input_and_signal.second, input_state);
681                 }
682                 for (const auto &input_and_filename : images_to_select) {
683                         input_and_filename.first->switch_image(input_and_filename.second);
684                 }
685                 for (const auto &effect_and_key_and_value : int_to_set) {
686                         Effect *effect = effect_and_key_and_value.first.first;
687                         const string &key = effect_and_key_and_value.first.second;
688                         const int value = effect_and_key_and_value.second;
689                         if (!effect->set_int(key, value)) {
690                                 luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), value);
691                         }
692                 }
693                 for (const auto &effect_and_key_and_value : float_to_set) {
694                         Effect *effect = effect_and_key_and_value.first.first;
695                         const string &key = effect_and_key_and_value.first.second;
696                         const float value = effect_and_key_and_value.second;
697                         if (!effect->set_float(key, value)) {
698                                 luaL_error(L, "Effect refused set_float(\"%s\", %f) (invalid key?)", key.c_str(), value);
699                         }
700                 }
701                 for (const auto &effect_and_key_and_value : vec3_to_set) {
702                         Effect *effect = effect_and_key_and_value.first.first;
703                         const string &key = effect_and_key_and_value.first.second;
704                         const float *value = effect_and_key_and_value.second.data();
705                         if (!effect->set_vec3(key, value)) {
706                                 luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(),
707                                                 value[0], value[1], value[2]);
708                         }
709                 }
710                 for (const auto &effect_and_key_and_value : vec4_to_set) {
711                         Effect *effect = effect_and_key_and_value.first.first;
712                         const string &key = effect_and_key_and_value.first.second;
713                         const float *value = effect_and_key_and_value.second.data();
714                         if (!effect->set_vec4(key, value)) {
715                                 luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(),
716                                                 value[0], value[1], value[2], value[3]);
717                         }
718                 }
719         };
720         return make_pair(effect_chain, move(setup_chain));
721 }
722
723 bool display(Block *block, lua_State *L, int idx)
724 {
725         if (lua_isnumber(L, idx)) {
726                 Theme *theme = get_theme_updata(L);
727                 int signal_idx = luaL_checknumber(L, idx);
728                 block->signal_type_to_connect = Block::CONNECT_SIGNAL;
729                 block->signal_to_connect = theme->map_signal(signal_idx);
730                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR);  // Will be changed to deinterlaced at get_chain() time if needed.
731                 return true;
732 #ifdef HAVE_CEF
733         } else if (luaL_testudata(L, idx, "HTMLInput")) {
734                 CEFCapture *capture = *(CEFCapture **)luaL_checkudata(L, idx, "HTMLInput");
735                 block->signal_type_to_connect = Block::CONNECT_CEF;
736                 block->cef_to_connect = capture;
737                 block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_BGRA);
738                 assert(capture->get_current_pixel_format() == bmusb::PixelFormat_8BitBGRA);
739                 return true;
740 #endif
741         } else if (luaL_testudata(L, idx, "VideoInput")) {
742                 FFmpegCapture *capture = *(FFmpegCapture **)luaL_checkudata(L, idx, "VideoInput");
743                 block->signal_type_to_connect = Block::CONNECT_VIDEO;
744                 block->video_to_connect = capture;
745                 if (capture->get_current_pixel_format() == bmusb::PixelFormat_8BitYCbCrPlanar) {
746                         block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_YCBCR_PLANAR);
747                 } else {
748                         assert(capture->get_current_pixel_format() == bmusb::PixelFormat_8BitBGRA);
749                         block->currently_chosen_alternative = find_index_of(block, LIVE_INPUT_BGRA);
750                 }
751                 return true;
752         } else if (luaL_testudata(L, idx, "ImageInput")) {
753                 ImageInput *image = *(ImageInput **)luaL_checkudata(L, idx, "ImageInput");
754                 block->signal_type_to_connect = Block::CONNECT_NONE;
755                 block->currently_chosen_alternative = find_index_of(block, IMAGE_INPUT);
756                 block->pathname = image->get_pathname();
757                 return true;
758         } else {
759                 return false;
760         }
761 }
762
763 int Block_display(lua_State* L)
764 {
765         assert(lua_gettop(L) == 2);
766         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
767         if (!block->is_input) {
768                 luaL_error(L, "display() called on something that isn't an input");
769         }
770
771         bool ok = display(block, L, 2);
772         if (!ok) {
773                 luaL_error(L, "display() called with something that's not a signal (a signal number, a HTML input, or a VideoInput)");
774         }
775
776         if (block->currently_chosen_alternative == -1) {
777                 luaL_error(L, "display() called on an input whose type was fixed at construction time, with a signal of different type");
778         }
779
780         return 0;
781 }
782
783 int Block_choose(lua_State* L)
784 {
785         assert(lua_gettop(L) == 2);
786         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
787         int alternative_idx = -1;
788         if (lua_isnumber(L, 2)) {
789                 alternative_idx = luaL_checknumber(L, 2);
790         } else if (lua_istable(L, 2)) {
791                 // See if it's an Effect metatable (e.g. foo:choose(ResampleEffect))
792                 lua_getfield(L, 2, "__effect_type_id");
793                 if (lua_isnumber(L, -1)) {
794                         EffectType effect_type = EffectType(luaL_checknumber(L, -1));
795                         alternative_idx = find_index_of(block, effect_type);
796                 }
797                 lua_pop(L, 1);
798         }
799
800         if (alternative_idx == -1) {
801                 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");
802         }
803
804         assert(alternative_idx >= 0);
805         assert(size_t(alternative_idx) < block->alternatives.size());
806         block->currently_chosen_alternative = alternative_idx;
807
808         return wrap_lua_existing_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", block->alternatives[alternative_idx]);
809 }
810
811 int Block_enable(lua_State *L)
812 {
813         assert(lua_gettop(L) == 1);
814         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
815
816         if (block->alternatives.size() != 2 ||
817             block->alternatives[1]->effect_type != IDENTITY_EFFECT) {
818                 luaL_error(L, "enable() called on something that wasn't added with add_optional_effect()");
819         }
820         block->currently_chosen_alternative = 0;  // The actual effect.
821         return 0;
822 }
823
824 int Block_enable_if(lua_State *L)
825 {
826         assert(lua_gettop(L) == 2);
827         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
828
829         if (block->alternatives.size() != 2 ||
830             block->alternatives[1]->effect_type != IDENTITY_EFFECT) {
831                 luaL_error(L, "enable_if() called on something that wasn't added with add_optional_effect()");
832         }
833         bool enabled = checkbool(L, 2);
834         block->currently_chosen_alternative = enabled ? 0 : 1;
835         return 0;
836 }
837
838 int Block_disable(lua_State *L)
839 {
840         assert(lua_gettop(L) == 1);
841         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
842
843         block->currently_chosen_alternative = find_index_of(block, IDENTITY_EFFECT);
844         if (block->currently_chosen_alternative == -1) {
845                 luaL_error(L, "disable() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
846         }
847         assert(block->currently_chosen_alternative != -1);
848         return 0;
849 }
850
851 int Block_always_disable_if_disabled(lua_State *L)
852 {
853         assert(lua_gettop(L) == 2);
854         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
855         Block *disabler_block = *(Block **)luaL_checkudata(L, 2, "Block");
856
857         int my_alternative = find_index_of(block, IDENTITY_EFFECT);
858         int their_alternative = find_index_of(disabler_block, IDENTITY_EFFECT);
859         if (my_alternative == -1) {
860                 luaL_error(L, "always_disable_if_disabled() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
861         }
862         if (their_alternative == -1) {
863                 luaL_error(L, "always_disable_if_disabled() with an argument that didn't have an IdentityEffect fallback (try add_optional_effect())");
864         }
865
866         // The declaration point isn't actually used, but it's nice for completeness.
867         block->disablers.push_back(Block::Disabler{ disabler_block->idx, Block::Disabler::DISABLE_IF_OTHER_DISABLED, get_declaration_point(L) });
868
869         lua_pop(L, 2);
870         return 0;
871 }
872
873 int Block_promise_to_disable_if_enabled(lua_State *L)
874 {
875         assert(lua_gettop(L) == 2);
876         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
877         Block *disabler_block = *(Block **)luaL_checkudata(L, 2, "Block");
878
879         int my_alternative = find_index_of(block, IDENTITY_EFFECT);
880         int their_alternative = find_index_of(disabler_block, IDENTITY_EFFECT);
881         if (my_alternative == -1) {
882                 luaL_error(L, "promise_to_disable_if_enabled() called on something that didn't have an IdentityEffect fallback (try add_optional_effect())");
883         }
884         if (their_alternative == -1) {
885                 luaL_error(L, "promise_to_disable_if_enabled() with an argument that didn't have an IdentityEffect fallback (try add_optional_effect())");
886         }
887
888         block->disablers.push_back(Block::Disabler{ disabler_block->idx, Block::Disabler::DISABLE_IF_OTHER_ENABLED, get_declaration_point(L) });
889
890         lua_pop(L, 2);
891         return 0;
892 }
893
894 int Block_set_int(lua_State *L)
895 {
896         assert(lua_gettop(L) == 3);
897         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
898         string key = checkstdstring(L, 2);
899         float value = luaL_checknumber(L, 3);
900
901         // TODO: check validity already here, if possible?
902         block->int_parameters[key] = value;
903
904         return 0;
905 }
906
907 int Block_set_float(lua_State *L)
908 {
909         assert(lua_gettop(L) == 3);
910         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
911         string key = checkstdstring(L, 2);
912         float value = luaL_checknumber(L, 3);
913
914         // TODO: check validity already here, if possible?
915         block->float_parameters[key] = value;
916
917         return 0;
918 }
919
920 int Block_set_vec3(lua_State *L)
921 {
922         assert(lua_gettop(L) == 5);
923         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
924         string key = checkstdstring(L, 2);
925         array<float, 3> v;
926         v[0] = luaL_checknumber(L, 3);
927         v[1] = luaL_checknumber(L, 4);
928         v[2] = luaL_checknumber(L, 5);
929
930         // TODO: check validity already here, if possible?
931         block->vec3_parameters[key] = v;
932
933         return 0;
934 }
935
936 int Block_set_vec4(lua_State *L)
937 {
938         assert(lua_gettop(L) == 6);
939         Block *block = *(Block **)luaL_checkudata(L, 1, "Block");
940         string key = checkstdstring(L, 2);
941         array<float, 4> v;
942         v[0] = luaL_checknumber(L, 3);
943         v[1] = luaL_checknumber(L, 4);
944         v[2] = luaL_checknumber(L, 5);
945         v[3] = luaL_checknumber(L, 6);
946
947         // TODO: check validity already here, if possible?
948         block->vec4_parameters[key] = v;
949
950         return 0;
951 }
952