]> git.sesse.net Git - nageru/blob - nageru/theme.cpp
f3e1aa1c64bb53cdc913e7f5fdb6d326362ff860
[nageru] / nageru / theme.cpp
1 #include "theme.h"
2
3 #include <assert.h>
4 #include <bmusb/bmusb.h>
5 #include <epoxy/gl.h>
6 #include <stdarg.h>
7 #include <lauxlib.h>
8 #include <lua.hpp>
9 #include <movit/deinterlace_effect.h>
10 #include <movit/effect.h>
11 #include <movit/effect_chain.h>
12 #include <movit/image_format.h>
13 #include <movit/input.h>
14 #include <movit/lift_gamma_gain_effect.h>
15 #include <movit/mix_effect.h>
16 #include <movit/multiply_effect.h>
17 #include <movit/overlay_effect.h>
18 #include <movit/padding_effect.h>
19 #include <movit/resample_effect.h>
20 #include <movit/resize_effect.h>
21 #include <movit/util.h>
22 #include <movit/white_balance_effect.h>
23 #include <movit/ycbcr.h>
24 #include <movit/ycbcr_input.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <cstddef>
28 #include <memory>
29 #include <new>
30 #include <utility>
31
32 #include "defs.h"
33 #ifdef HAVE_CEF
34 #include "cef_capture.h"
35 #endif
36 #include "ffmpeg_capture.h"
37 #include "flags.h"
38 #include "image_input.h"
39 #include "input_state.h"
40 #include "lua_utils.h"
41 #include "pbo_frame_allocator.h"
42 #include "scene.h"
43
44 class Mixer;
45
46 namespace movit {
47 class ResourcePool;
48 }  // namespace movit
49
50 using namespace std;
51 using namespace movit;
52
53 extern Mixer *global_mixer;
54
55 Theme *get_theme_updata(lua_State* L)
56 {
57         luaL_checktype(L, lua_upvalueindex(1), LUA_TLIGHTUSERDATA);
58         return (Theme *)lua_touserdata(L, lua_upvalueindex(1));
59 }
60
61 void print_warning(lua_State* L, const char *format, ...)
62 {
63         char buf[4096];
64         va_list ap;
65         va_start(ap, format);
66         vsnprintf(buf, sizeof(buf), format, ap);
67         va_end(ap);
68
69         lua_Debug ar;
70         lua_getstack(L, 1, &ar);
71         lua_getinfo(L, "nSl", &ar);
72         fprintf(stderr, "WARNING: %s:%d: %s", ar.source, ar.currentline, buf);
73 }
74
75 int ThemeMenu_set(lua_State *L)
76 {
77         Theme *theme = get_theme_updata(L);
78         return theme->set_theme_menu(L);
79 }
80
81 InputStateInfo::InputStateInfo(const InputState &input_state)
82 {
83         for (unsigned signal_num = 0; signal_num < MAX_VIDEO_CARDS; ++signal_num) {
84                 BufferedFrame frame = input_state.buffered_frames[signal_num][0];
85                 if (frame.frame == nullptr) {
86                         last_width[signal_num] = last_height[signal_num] = 0;
87                         last_interlaced[signal_num] = false;
88                         last_has_signal[signal_num] = false;
89                         last_is_connected[signal_num] = false;
90                         continue;
91                 }
92                 const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
93                 last_width[signal_num] = userdata->last_width[frame.field_number];
94                 last_height[signal_num] = userdata->last_height[frame.field_number];
95                 last_interlaced[signal_num] = userdata->last_interlaced;
96                 last_has_signal[signal_num] = userdata->last_has_signal;
97                 last_is_connected[signal_num] = userdata->last_is_connected;
98                 last_frame_rate_nom[signal_num] = userdata->last_frame_rate_nom;
99                 last_frame_rate_den[signal_num] = userdata->last_frame_rate_den;
100                 has_last_subtitle[signal_num] = userdata->has_last_subtitle;
101                 last_subtitle[signal_num] = userdata->last_subtitle;
102         }
103 }
104
105 // An effect that does nothing.
106 class IdentityEffect : public Effect {
107 public:
108         IdentityEffect() {}
109         string effect_type_id() const override { return "IdentityEffect"; }
110         string output_fragment_shader() override { return read_file("identity.frag"); }
111 };
112
113 Effect *instantiate_effect(EffectChain *chain, EffectType effect_type)
114 {
115         switch (effect_type) {
116         case IDENTITY_EFFECT:
117                 return new IdentityEffect;
118         case WHITE_BALANCE_EFFECT:
119         case AUTO_WHITE_BALANCE_EFFECT:
120                 return new WhiteBalanceEffect;
121         case RESAMPLE_EFFECT:
122                 return new ResampleEffect;
123         case PADDING_EFFECT:
124                 return new PaddingEffect;
125         case INTEGRAL_PADDING_EFFECT:
126                 return new IntegralPaddingEffect;
127         case OVERLAY_EFFECT:
128                 return new OverlayEffect;
129         case RESIZE_EFFECT:
130                 return new ResizeEffect;
131         case MULTIPLY_EFFECT:
132                 return new MultiplyEffect;
133         case MIX_EFFECT:
134                 return new MixEffect;
135         case LIFT_GAMMA_GAIN_EFFECT:
136                 return new LiftGammaGainEffect;
137         default:
138                 fprintf(stderr, "Unhandled effect type %d\n", effect_type);
139                 abort();
140         }
141 }
142
143 namespace {
144
145 Effect *get_effect_from_blueprint(EffectChain *chain, lua_State *L, int idx)
146 {
147         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint");
148         if (blueprint->effect != nullptr) {
149                 luaL_error(L, "An effect can currently only be added to one chain.\n");
150         }
151
152         Effect *effect = instantiate_effect(chain, blueprint->effect_type);
153
154         // Set the parameters that were deferred earlier.
155         for (const auto &kv : blueprint->int_parameters) {
156                 if (!effect->set_int(kv.first, kv.second)) {
157                         luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", kv.first.c_str(), kv.second);
158                 }
159         }
160         for (const auto &kv : blueprint->float_parameters) {
161                 if (!effect->set_float(kv.first, kv.second)) {
162                         luaL_error(L, "Effect refused set_float(\"%s\", %f) (invalid key?)", kv.first.c_str(), kv.second);
163                 }
164         }
165         for (const auto &kv : blueprint->vec3_parameters) {
166                 if (!effect->set_vec3(kv.first, kv.second.data())) {
167                         luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", kv.first.c_str(),
168                                 kv.second[0], kv.second[1], kv.second[2]);
169                 }
170         }
171         for (const auto &kv : blueprint->vec4_parameters) {
172                 if (!effect->set_vec4(kv.first, kv.second.data())) {
173                         luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", kv.first.c_str(),
174                                 kv.second[0], kv.second[1], kv.second[2], kv.second[3]);
175                 }
176         }
177         blueprint->effect = effect;
178         return effect;
179 }
180
181 InputStateInfo *get_input_state_info(lua_State *L, int idx)
182 {
183         if (luaL_testudata(L, idx, "InputStateInfo")) {
184                 return (InputStateInfo *)lua_touserdata(L, idx);
185         }
186         luaL_error(L, "Error: Index #%d was not InputStateInfo\n", idx);
187         return nullptr;
188 }
189
190 }  // namespace
191
192 bool checkbool(lua_State* L, int idx)
193 {
194         luaL_checktype(L, idx, LUA_TBOOLEAN);
195         return lua_toboolean(L, idx);
196 }
197
198 string checkstdstring(lua_State *L, int index)
199 {
200         size_t len;
201         const char* cstr = lua_tolstring(L, index, &len);
202         return string(cstr, len);
203 }
204
205 namespace {
206
207 int Scene_new(lua_State* L)
208 {
209         assert(lua_gettop(L) == 2);
210         Theme *theme = get_theme_updata(L);
211         int aspect_w = luaL_checknumber(L, 1);
212         int aspect_h = luaL_checknumber(L, 2);
213
214         return wrap_lua_object<Scene>(L, "Scene", theme, aspect_w, aspect_h);
215 }
216
217 int Scene_gc(lua_State* L)
218 {
219         assert(lua_gettop(L) == 1);
220         Scene *chain = (Scene *)luaL_checkudata(L, 1, "Scene");
221         chain->~Scene();
222         return 0;
223 }
224
225 }  // namespace
226
227 void add_outputs_and_finalize(EffectChain *chain, bool is_main_chain)
228 {
229         // Add outputs as needed.
230         // NOTE: If you change any details about the output format, you will need to
231         // also update what's given to the muxer (HTTPD::Mux constructor) and
232         // what's put in the H.264 stream (sps_rbsp()).
233         ImageFormat inout_format;
234         inout_format.color_space = COLORSPACE_REC_709;
235
236         // Output gamma is tricky. We should output Rec. 709 for TV, except that
237         // we expect to run with web players and others that don't really care and
238         // just output with no conversion. So that means we'll need to output sRGB,
239         // even though H.264 has no setting for that (we use “unspecified”).
240         inout_format.gamma_curve = GAMMA_sRGB;
241
242         if (is_main_chain) {
243                 YCbCrFormat output_ycbcr_format;
244                 // We actually output 4:2:0 and/or 4:2:2 in the end, but chroma subsampling
245                 // happens in a pass not run by Movit (see ChromaSubsampler::subsample_chroma()).
246                 output_ycbcr_format.chroma_subsampling_x = 1;
247                 output_ycbcr_format.chroma_subsampling_y = 1;
248
249                 // This will be overridden if HDMI/SDI output is in force.
250                 if (global_flags.ycbcr_rec709_coefficients) {
251                         output_ycbcr_format.luma_coefficients = YCBCR_REC_709;
252                 } else {
253                         output_ycbcr_format.luma_coefficients = YCBCR_REC_601;
254                 }
255
256                 output_ycbcr_format.full_range = false;
257                 output_ycbcr_format.num_levels = 1 << global_flags.x264_bit_depth;
258
259                 GLenum type = global_flags.x264_bit_depth > 8 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
260
261                 chain->add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, output_ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR, type);
262
263                 // If we're using zerocopy video encoding (so the destination
264                 // Y texture is owned by VA-API and will be unavailable for
265                 // display), add a copy, where we'll only be using the Y component.
266                 if (global_flags.use_zerocopy) {
267                         chain->add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, output_ycbcr_format, YCBCR_OUTPUT_INTERLEAVED, type);  // Add a copy where we'll only be using the Y component.
268                 }
269                 chain->set_dither_bits(global_flags.x264_bit_depth > 8 ? 16 : 8);
270                 chain->set_output_origin(OUTPUT_ORIGIN_TOP_LEFT);
271         } else {
272                 chain->add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
273         }
274
275         chain->finalize();
276 }
277
278 namespace {
279
280 int EffectChain_new(lua_State* L)
281 {
282         assert(lua_gettop(L) == 2);
283         Theme *theme = get_theme_updata(L);
284         int aspect_w = luaL_checknumber(L, 1);
285         int aspect_h = luaL_checknumber(L, 2);
286
287         return wrap_lua_object<EffectChain>(L, "EffectChain", aspect_w, aspect_h, theme->get_resource_pool());
288 }
289
290 int EffectChain_gc(lua_State* L)
291 {
292         assert(lua_gettop(L) == 1);
293         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
294         chain->~EffectChain();
295         return 0;
296 }
297
298 int EffectChain_add_live_input(lua_State* L)
299 {
300         assert(lua_gettop(L) == 3);
301         Theme *theme = get_theme_updata(L);
302         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
303         bool override_bounce = checkbool(L, 2);
304         bool deinterlace = checkbool(L, 3);
305         bmusb::PixelFormat pixel_format = global_flags.ten_bit_input ? bmusb::PixelFormat_10BitYCbCr : bmusb::PixelFormat_8BitYCbCr;
306
307         // Needs to be nonowned to match add_video_input (see below).
308         return wrap_lua_object_nonowned<LiveInputWrapper>(L, "LiveInputWrapper", theme, chain, pixel_format, override_bounce, deinterlace, /*user_connectable=*/true);
309 }
310
311 int EffectChain_add_video_input(lua_State* L)
312 {
313         assert(lua_gettop(L) == 3);
314         Theme *theme = get_theme_updata(L);
315         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
316         FFmpegCapture **capture = (FFmpegCapture **)luaL_checkudata(L, 2, "VideoInput");
317         bool deinterlace = checkbool(L, 3);
318
319         // These need to be nonowned, so that the LiveInputWrapper still exists
320         // and can feed frames to the right EffectChain even if the Lua code
321         // doesn't care about the object anymore. (If we change this, we'd need
322         // to also unregister the signal connection on __gc.)
323         int ret = wrap_lua_object_nonowned<LiveInputWrapper>(
324                 L, "LiveInputWrapper", theme, chain, (*capture)->get_current_pixel_format(),
325                 /*override_bounce=*/false, deinterlace, /*user_connectable=*/false);
326         if (ret == 1) {
327                 Theme *theme = get_theme_updata(L);
328                 LiveInputWrapper **live_input = (LiveInputWrapper **)lua_touserdata(L, -1);
329                 theme->register_video_signal_connection(chain, *live_input, *capture);
330         }
331         return ret;
332 }
333
334 #ifdef HAVE_CEF
335 int EffectChain_add_html_input(lua_State* L)
336 {
337         assert(lua_gettop(L) == 2);
338         Theme *theme = get_theme_updata(L);
339         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
340         CEFCapture **capture = (CEFCapture **)luaL_checkudata(L, 2, "HTMLInput");
341
342         // These need to be nonowned, so that the LiveInputWrapper still exists
343         // and can feed frames to the right EffectChain even if the Lua code
344         // doesn't care about the object anymore. (If we change this, we'd need
345         // to also unregister the signal connection on __gc.)
346         int ret = wrap_lua_object_nonowned<LiveInputWrapper>(
347                 L, "LiveInputWrapper", theme, chain, (*capture)->get_current_pixel_format(),
348                 /*override_bounce=*/false, /*deinterlace=*/false, /*user_connectable=*/false);
349         if (ret == 1) {
350                 Theme *theme = get_theme_updata(L);
351                 LiveInputWrapper **live_input = (LiveInputWrapper **)lua_touserdata(L, -1);
352                 theme->register_html_signal_connection(chain, *live_input, *capture);
353         }
354         return ret;
355 }
356 #endif
357
358 int EffectChain_add_effect(lua_State* L)
359 {
360         assert(lua_gettop(L) >= 2);
361         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
362
363         // TODO: Better error reporting.
364         Effect *effect;
365         if (luaL_testudata(L, 2, "ImageInput")) {
366                 effect = *(ImageInput **)luaL_checkudata(L, 2, "ImageInput");
367         } else {
368                 effect = get_effect_from_blueprint(chain, L, 2);
369         }
370         if (lua_gettop(L) == 2) {
371                 if (effect->num_inputs() == 0) {
372                         chain->add_input((Input *)effect);
373                 } else {
374                         chain->add_effect(effect);
375                 }
376         } else {
377                 vector<Effect *> inputs;
378                 for (int idx = 3; idx <= lua_gettop(L); ++idx) {
379                         if (luaL_testudata(L, idx, "LiveInputWrapper")) {
380                                 LiveInputWrapper **input = (LiveInputWrapper **)lua_touserdata(L, idx);
381                                 inputs.push_back((*input)->get_effect());
382                         } else if (luaL_testudata(L, idx, "ImageInput")) {
383                                 ImageInput *image = *(ImageInput **)luaL_checkudata(L, idx, "ImageInput");
384                                 inputs.push_back(image);
385                         } else {
386                                 EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint");
387                                 assert(blueprint->effect != nullptr);  // Parent must be added to the graph.
388                                 inputs.push_back(blueprint->effect);
389                         }
390                 }
391                 chain->add_effect(effect, inputs);
392         }
393
394         lua_settop(L, 2);  // Return the effect itself.
395
396         // Make sure Lua doesn't garbage-collect it away.
397         lua_pushvalue(L, -1);
398         luaL_ref(L, LUA_REGISTRYINDEX);  // TODO: leak?
399
400         return 1;
401 }
402
403 int EffectChain_finalize(lua_State* L)
404 {
405         assert(lua_gettop(L) == 2);
406         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
407         bool is_main_chain = checkbool(L, 2);
408         add_outputs_and_finalize(chain, is_main_chain);
409         return 0;
410 }
411
412 int LiveInputWrapper_connect_signal(lua_State* L)
413 {
414         assert(lua_gettop(L) == 2);
415         LiveInputWrapper **input = (LiveInputWrapper **)luaL_checkudata(L, 1, "LiveInputWrapper");
416         int signal_num = luaL_checknumber(L, 2);
417         bool success = (*input)->connect_signal(signal_num);
418         if (!success) {
419                 print_warning(L, "Calling connect_signal() on a video or HTML input. Ignoring.\n");
420         }
421         return 0;
422 }
423
424 int ImageInput_new(lua_State* L)
425 {
426         assert(lua_gettop(L) == 1);
427         string filename = checkstdstring(L, 1);
428         return wrap_lua_object_nonowned<ImageInput>(L, "ImageInput", filename);
429 }
430
431 int VideoInput_new(lua_State* L)
432 {
433         assert(lua_gettop(L) == 2);
434         string filename = checkstdstring(L, 1);
435         int pixel_format = luaL_checknumber(L, 2);
436         if (pixel_format != bmusb::PixelFormat_8BitYCbCrPlanar &&
437             pixel_format != bmusb::PixelFormat_8BitBGRA) {
438                 print_warning(L, "Invalid enum %d used for video format, choosing Y'CbCr.\n", pixel_format);
439                 pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar;
440         }
441         int ret = wrap_lua_object_nonowned<FFmpegCapture>(L, "VideoInput", filename, global_flags.width, global_flags.height);
442         if (ret == 1) {
443                 FFmpegCapture **capture = (FFmpegCapture **)lua_touserdata(L, -1);
444                 (*capture)->set_pixel_format(bmusb::PixelFormat(pixel_format));
445
446                 Theme *theme = get_theme_updata(L);
447                 theme->register_video_input(*capture);
448         }
449         return ret;
450 }
451
452 int VideoInput_rewind(lua_State* L)
453 {
454         assert(lua_gettop(L) == 1);
455         FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput");
456         (*video_input)->rewind();
457         return 0;
458 }
459
460 int VideoInput_disconnect(lua_State* L)
461 {
462         assert(lua_gettop(L) == 1);
463         FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput");
464         (*video_input)->disconnect();
465         return 0;
466 }
467
468 int VideoInput_change_rate(lua_State* L)
469 {
470         assert(lua_gettop(L) == 2);
471         FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput");
472         double new_rate = luaL_checknumber(L, 2);
473         (*video_input)->change_rate(new_rate);
474         return 0;
475 }
476
477 int VideoInput_get_signal_num(lua_State* L)
478 {
479         assert(lua_gettop(L) == 1);
480         FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput");
481         lua_pushnumber(L, -1 - (*video_input)->get_card_index());
482         return 1;
483 }
484
485 int HTMLInput_new(lua_State* L)
486 {
487 #ifdef HAVE_CEF
488         assert(lua_gettop(L) == 1);
489         string url = checkstdstring(L, 1);
490         int ret = wrap_lua_object_nonowned<CEFCapture>(L, "HTMLInput", url, global_flags.width, global_flags.height);
491         if (ret == 1) {
492                 CEFCapture **capture = (CEFCapture **)lua_touserdata(L, -1);
493                 Theme *theme = get_theme_updata(L);
494                 theme->register_html_input(*capture);
495         }
496         return ret;
497 #else
498         fprintf(stderr, "This version of Nageru has been compiled without CEF support.\n");
499         fprintf(stderr, "HTMLInput is not available.\n");
500         abort();
501 #endif
502 }
503
504 #ifdef HAVE_CEF
505 int HTMLInput_set_url(lua_State* L)
506 {
507         assert(lua_gettop(L) == 2);
508         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
509         string new_url = checkstdstring(L, 2);
510         (*video_input)->set_url(new_url);
511         return 0;
512 }
513
514 int HTMLInput_reload(lua_State* L)
515 {
516         assert(lua_gettop(L) == 1);
517         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
518         (*video_input)->reload();
519         return 0;
520 }
521
522 int HTMLInput_set_max_fps(lua_State* L)
523 {
524         assert(lua_gettop(L) == 2);
525         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
526         int max_fps = lrint(luaL_checknumber(L, 2));
527         (*video_input)->set_max_fps(max_fps);
528         return 0;
529 }
530
531 int HTMLInput_execute_javascript_async(lua_State* L)
532 {
533         assert(lua_gettop(L) == 2);
534         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
535         string js = checkstdstring(L, 2);
536         (*video_input)->execute_javascript_async(js);
537         return 0;
538 }
539
540 int HTMLInput_resize(lua_State* L)
541 {
542         assert(lua_gettop(L) == 3);
543         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
544         unsigned width = lrint(luaL_checknumber(L, 2));
545         unsigned height = lrint(luaL_checknumber(L, 3));
546         (*video_input)->resize(width, height);
547         return 0;
548 }
549
550 int HTMLInput_get_signal_num(lua_State* L)
551 {
552         assert(lua_gettop(L) == 1);
553         CEFCapture **video_input = (CEFCapture **)luaL_checkudata(L, 1, "HTMLInput");
554         lua_pushnumber(L, -1 - (*video_input)->get_card_index());
555         return 1;
556 }
557 #endif
558
559 int IdentityEffect_new(lua_State* L)
560 {
561         assert(lua_gettop(L) == 0);
562         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", IDENTITY_EFFECT);
563 }
564
565 int WhiteBalanceEffect_new(lua_State* L)
566 {
567         assert(lua_gettop(L) == 0);
568         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", WHITE_BALANCE_EFFECT);
569 }
570
571 int ResampleEffect_new(lua_State* L)
572 {
573         assert(lua_gettop(L) == 0);
574         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", RESAMPLE_EFFECT);
575 }
576
577 int PaddingEffect_new(lua_State* L)
578 {
579         assert(lua_gettop(L) == 0);
580         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", PADDING_EFFECT);
581 }
582
583 int IntegralPaddingEffect_new(lua_State* L)
584 {
585         assert(lua_gettop(L) == 0);
586         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", INTEGRAL_PADDING_EFFECT);
587 }
588
589 int OverlayEffect_new(lua_State* L)
590 {
591         assert(lua_gettop(L) == 0);
592         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", OVERLAY_EFFECT);
593 }
594
595 int ResizeEffect_new(lua_State* L)
596 {
597         assert(lua_gettop(L) == 0);
598         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", RESIZE_EFFECT);
599 }
600
601 int MultiplyEffect_new(lua_State* L)
602 {
603         assert(lua_gettop(L) == 0);
604         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", MULTIPLY_EFFECT);
605 }
606
607 int MixEffect_new(lua_State* L)
608 {
609         assert(lua_gettop(L) == 0);
610         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", MIX_EFFECT);
611 }
612
613 int LiftGammaGainEffect_new(lua_State* L)
614 {
615         assert(lua_gettop(L) == 0);
616         return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", LIFT_GAMMA_GAIN_EFFECT);
617 }
618
619 int InputStateInfo_get_width(lua_State* L)
620 {
621         assert(lua_gettop(L) == 2);
622         InputStateInfo *input_state_info = get_input_state_info(L, 1);
623
624         Theme *theme = get_theme_updata(L);
625         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
626         lua_pushnumber(L, input_state_info->last_width[signal_num]);
627         return 1;
628 }
629
630 int InputStateInfo_get_height(lua_State* L)
631 {
632         assert(lua_gettop(L) == 2);
633         InputStateInfo *input_state_info = get_input_state_info(L, 1);
634         Theme *theme = get_theme_updata(L);
635         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
636         lua_pushnumber(L, input_state_info->last_height[signal_num]);
637         return 1;
638 }
639
640 int InputStateInfo_get_frame_height(lua_State* L)
641 {
642         assert(lua_gettop(L) == 2);
643         InputStateInfo *input_state_info = get_input_state_info(L, 1);
644         Theme *theme = get_theme_updata(L);
645         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
646         unsigned height = input_state_info->last_height[signal_num];
647         if (input_state_info->last_interlaced[signal_num]) {
648                 height *= 2;
649         }
650         lua_pushnumber(L, height);
651         return 1;
652 }
653
654 int InputStateInfo_get_interlaced(lua_State* L)
655 {
656         assert(lua_gettop(L) == 2);
657         InputStateInfo *input_state_info = get_input_state_info(L, 1);
658         Theme *theme = get_theme_updata(L);
659         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
660         lua_pushboolean(L, input_state_info->last_interlaced[signal_num]);
661         return 1;
662 }
663
664 int InputStateInfo_get_has_signal(lua_State* L)
665 {
666         assert(lua_gettop(L) == 2);
667         InputStateInfo *input_state_info = get_input_state_info(L, 1);
668         Theme *theme = get_theme_updata(L);
669         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
670         lua_pushboolean(L, input_state_info->last_has_signal[signal_num]);
671         return 1;
672 }
673
674 int InputStateInfo_get_is_connected(lua_State* L)
675 {
676         assert(lua_gettop(L) == 2);
677         InputStateInfo *input_state_info = get_input_state_info(L, 1);
678         Theme *theme = get_theme_updata(L);
679         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
680         lua_pushboolean(L, input_state_info->last_is_connected[signal_num]);
681         return 1;
682 }
683
684 int InputStateInfo_get_frame_rate_nom(lua_State* L)
685 {
686         assert(lua_gettop(L) == 2);
687         InputStateInfo *input_state_info = get_input_state_info(L, 1);
688         Theme *theme = get_theme_updata(L);
689         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
690         lua_pushnumber(L, input_state_info->last_frame_rate_nom[signal_num]);
691         return 1;
692 }
693
694 int InputStateInfo_get_frame_rate_den(lua_State* L)
695 {
696         assert(lua_gettop(L) == 2);
697         InputStateInfo *input_state_info = get_input_state_info(L, 1);
698         Theme *theme = get_theme_updata(L);
699         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
700         lua_pushnumber(L, input_state_info->last_frame_rate_den[signal_num]);
701         return 1;
702 }
703
704 int InputStateInfo_get_last_subtitle(lua_State* L)
705 {
706         assert(lua_gettop(L) == 2);
707         InputStateInfo *input_state_info = get_input_state_info(L, 1);
708         Theme *theme = get_theme_updata(L);
709         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
710         if (!input_state_info->has_last_subtitle[signal_num]) {
711                 lua_pushnil(L);
712         } else {
713                 lua_pushstring(L, input_state_info->last_subtitle[signal_num].c_str());
714         }
715         return 1;
716 }
717
718 namespace {
719
720 // Helper function to write e.g. “60” or “59.94”.
721 string format_frame_rate(int nom, int den)
722 {
723         char buf[256];
724         if (nom % den == 0) {
725                 snprintf(buf, sizeof(buf), "%d", nom / den);
726         } else {
727                 snprintf(buf, sizeof(buf), "%.2f", double(nom) / den);
728         }
729         return buf;
730 }
731
732 // Helper function to write e.g. “720p60”.
733 string get_human_readable_resolution(const InputStateInfo *input_state_info, int signal_num)
734 {
735         char buf[256];
736         if (input_state_info->last_interlaced[signal_num]) {
737                 snprintf(buf, sizeof(buf), "%di", input_state_info->last_height[signal_num] * 2);
738
739                 // Show field rate instead of frame rate; really for cosmetics only
740                 // (and actually contrary to EBU recommendations, although in line
741                 // with typical user expectations).
742                 return buf + format_frame_rate(input_state_info->last_frame_rate_nom[signal_num] * 2,
743                         input_state_info->last_frame_rate_den[signal_num]);
744         } else {
745                 snprintf(buf, sizeof(buf), "%dp", input_state_info->last_height[signal_num]);
746                 return buf + format_frame_rate(input_state_info->last_frame_rate_nom[signal_num],
747                         input_state_info->last_frame_rate_den[signal_num]);
748         }
749 }
750
751 } // namespace
752
753 int InputStateInfo_get_human_readable_resolution(lua_State* L)
754 {
755         assert(lua_gettop(L) == 2);
756         InputStateInfo *input_state_info = get_input_state_info(L, 1);
757         Theme *theme = get_theme_updata(L);
758         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
759
760         string str;
761         if (!input_state_info->last_is_connected[signal_num]) {
762                 str = "disconnected";
763         } else if (input_state_info->last_height[signal_num] <= 0) {
764                 str = "no signal";
765         } else if (!input_state_info->last_has_signal[signal_num]) {
766                 if (input_state_info->last_height[signal_num] == 525) {
767                         // Special mode for the USB3 cards.
768                         str = "no signal";
769                 } else {
770                         str = get_human_readable_resolution(input_state_info, signal_num) + ", no signal";
771                 }
772         } else {
773                 str = get_human_readable_resolution(input_state_info, signal_num);
774         }
775
776         lua_pushstring(L, str.c_str());
777         return 1;
778 }
779
780
781 int EffectBlueprint_set_int(lua_State *L)
782 {
783         assert(lua_gettop(L) == 3);
784         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint");
785         string key = checkstdstring(L, 2);
786         float value = luaL_checknumber(L, 3);
787         if (blueprint->effect != nullptr) {
788                 if (!blueprint->effect->set_int(key, value)) {
789                         luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
790                 }
791         } else {
792                 // TODO: check validity already here, if possible?
793                 blueprint->int_parameters[key] = value;
794         }
795         return 0;
796 }
797
798 int EffectBlueprint_set_float(lua_State *L)
799 {
800         assert(lua_gettop(L) == 3);
801         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint");
802         string key = checkstdstring(L, 2);
803         float value = luaL_checknumber(L, 3);
804         if (blueprint->effect != nullptr) {
805                 if (!blueprint->effect->set_float(key, value)) {
806                         luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
807                 }
808         } else {
809                 // TODO: check validity already here, if possible?
810                 blueprint->float_parameters[key] = value;
811         }
812         return 0;
813 }
814
815 int EffectBlueprint_set_vec3(lua_State *L)
816 {
817         assert(lua_gettop(L) == 5);
818         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint");
819         string key = checkstdstring(L, 2);
820         array<float, 3> v;
821         v[0] = luaL_checknumber(L, 3);
822         v[1] = luaL_checknumber(L, 4);
823         v[2] = luaL_checknumber(L, 5);
824
825         if (blueprint->effect != nullptr) {
826                 if (!blueprint->effect->set_vec3(key, v.data())) {
827                         luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(),
828                                 v[0], v[1], v[2]);
829                 }
830         } else {
831                 // TODO: check validity already here, if possible?
832                 blueprint->vec3_parameters[key] = v;
833         }
834
835         return 0;
836 }
837
838 int EffectBlueprint_set_vec4(lua_State *L)
839 {
840         assert(lua_gettop(L) == 6);
841         EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint");
842         string key = checkstdstring(L, 2);
843         array<float, 4> v;
844         v[0] = luaL_checknumber(L, 3);
845         v[1] = luaL_checknumber(L, 4);
846         v[2] = luaL_checknumber(L, 5);
847         v[3] = luaL_checknumber(L, 6);
848         if (blueprint->effect != nullptr) {
849                 if (!blueprint->effect->set_vec4(key, v.data())) {
850                         luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(),
851                                 v[0], v[1], v[2], v[3]);
852                 }
853         } else {
854                 // TODO: check validity already here, if possible?
855                 blueprint->vec4_parameters[key] = v;
856         }
857         return 0;
858 }
859
860 const luaL_Reg Scene_funcs[] = {
861         { "new", Scene_new },
862         { "__gc", Scene_gc },
863         { "add_input", Scene::add_input },
864         { "add_auto_white_balance", Scene::add_auto_white_balance },
865         { "add_effect", Scene::add_effect },
866         { "add_optional_effect", Scene::add_optional_effect },
867         { "finalize", Scene::finalize },
868         { NULL, NULL }
869 };
870
871 const luaL_Reg Block_funcs[] = {
872         { "display", Block_display },
873         { "choose", Block_choose },
874         { "enable", Block_enable },
875         { "enable_if", Block_enable_if },
876         { "disable", Block_disable },
877         { "always_disable_if_disabled", Block_always_disable_if_disabled },
878         { "promise_to_disable_if_enabled", Block_promise_to_disable_if_enabled },
879         { "set_int", Block_set_int },
880         { "set_float", Block_set_float },
881         { "set_vec3", Block_set_vec3 },
882         { "set_vec4", Block_set_vec4 },
883         { NULL, NULL }
884 };
885
886 const luaL_Reg EffectBlueprint_funcs[] = {
887         // NOTE: No new() function; that's for the individual effects.
888         { "set_int", EffectBlueprint_set_int },
889         { "set_float", EffectBlueprint_set_float },
890         { "set_vec3", EffectBlueprint_set_vec3 },
891         { "set_vec4", EffectBlueprint_set_vec4 },
892         { NULL, NULL }
893 };
894
895 const luaL_Reg EffectChain_funcs[] = {
896         { "new", EffectChain_new },
897         { "__gc", EffectChain_gc },
898         { "add_live_input", EffectChain_add_live_input },
899         { "add_video_input", EffectChain_add_video_input },
900 #ifdef HAVE_CEF
901         { "add_html_input", EffectChain_add_html_input },
902 #endif
903         { "add_effect", EffectChain_add_effect },
904         { "finalize", EffectChain_finalize },
905         { NULL, NULL }
906 };
907
908 const luaL_Reg LiveInputWrapper_funcs[] = {
909         { "connect_signal", LiveInputWrapper_connect_signal },
910         { NULL, NULL }
911 };
912
913 const luaL_Reg ImageInput_funcs[] = {
914         { "new", ImageInput_new },
915         { NULL, NULL }
916 };
917
918 const luaL_Reg VideoInput_funcs[] = {
919         { "new", VideoInput_new },
920         { "rewind", VideoInput_rewind },
921         { "disconnect", VideoInput_disconnect },
922         { "change_rate", VideoInput_change_rate },
923         { "get_signal_num", VideoInput_get_signal_num },
924         { NULL, NULL }
925 };
926
927 const luaL_Reg HTMLInput_funcs[] = {
928         { "new", HTMLInput_new },
929 #ifdef HAVE_CEF
930         { "set_url", HTMLInput_set_url },
931         { "reload", HTMLInput_reload },
932         { "set_max_fps", HTMLInput_set_max_fps },
933         { "execute_javascript_async", HTMLInput_execute_javascript_async },
934         { "resize", HTMLInput_resize },
935         { "get_signal_num", HTMLInput_get_signal_num },
936 #endif
937         { NULL, NULL }
938 };
939
940 // Effects.
941 // All of these are solely for new(); the returned metatable will be that of
942 // EffectBlueprint, and Effect (returned from add_effect()) is its own type.
943
944 const luaL_Reg IdentityEffect_funcs[] = {
945         { "new", IdentityEffect_new },
946         { NULL, NULL }
947 };
948
949 const luaL_Reg WhiteBalanceEffect_funcs[] = {
950         { "new", WhiteBalanceEffect_new },
951         { NULL, NULL }
952 };
953
954 const luaL_Reg ResampleEffect_funcs[] = {
955         { "new", ResampleEffect_new },
956         { NULL, NULL }
957 };
958
959 const luaL_Reg PaddingEffect_funcs[] = {
960         { "new", PaddingEffect_new },
961         { NULL, NULL }
962 };
963
964 const luaL_Reg IntegralPaddingEffect_funcs[] = {
965         { "new", IntegralPaddingEffect_new },
966         { NULL, NULL }
967 };
968
969 const luaL_Reg OverlayEffect_funcs[] = {
970         { "new", OverlayEffect_new },
971         { NULL, NULL }
972 };
973
974 const luaL_Reg ResizeEffect_funcs[] = {
975         { "new", ResizeEffect_new },
976         { NULL, NULL }
977 };
978
979 const luaL_Reg MultiplyEffect_funcs[] = {
980         { "new", MultiplyEffect_new },
981         { NULL, NULL }
982 };
983
984 const luaL_Reg MixEffect_funcs[] = {
985         { "new", MixEffect_new },
986         { NULL, NULL }
987 };
988
989 const luaL_Reg LiftGammaGainEffect_funcs[] = {
990         { "new", LiftGammaGainEffect_new },
991         { NULL, NULL }
992 };
993
994 // End of effects.
995
996 const luaL_Reg InputStateInfo_funcs[] = {
997         { "get_width", InputStateInfo_get_width },
998         { "get_height", InputStateInfo_get_height },
999         { "get_frame_width", InputStateInfo_get_width },  // Same as get_width().
1000         { "get_frame_height", InputStateInfo_get_frame_height },
1001         { "get_interlaced", InputStateInfo_get_interlaced },
1002         { "get_has_signal", InputStateInfo_get_has_signal },
1003         { "get_is_connected", InputStateInfo_get_is_connected },
1004         { "get_frame_rate_nom", InputStateInfo_get_frame_rate_nom },
1005         { "get_frame_rate_den", InputStateInfo_get_frame_rate_den },
1006         { "get_last_subtitle", InputStateInfo_get_last_subtitle },
1007         { "get_human_readable_resolution", InputStateInfo_get_human_readable_resolution },
1008         { NULL, NULL }
1009 };
1010
1011 const luaL_Reg ThemeMenu_funcs[] = {
1012         { "set", ThemeMenu_set },
1013         { NULL, NULL }
1014 };
1015
1016 }  // namespace
1017
1018 LiveInputWrapper::LiveInputWrapper(
1019         Theme *theme,
1020         EffectChain *chain,
1021         bmusb::PixelFormat pixel_format,
1022         bool override_bounce,
1023         bool deinterlace,
1024         bool user_connectable)
1025         : theme(theme),
1026           pixel_format(pixel_format),
1027           deinterlace(deinterlace),
1028           user_connectable(user_connectable)
1029 {
1030         ImageFormat inout_format;
1031         inout_format.color_space = COLORSPACE_sRGB;
1032
1033         // Gamma curve depends on the input signal, and we don't really get any
1034         // indications. A camera would be expected to do Rec. 709, but
1035         // I haven't checked if any do in practice. However, computers _do_ output
1036         // in sRGB gamma (ie., they don't convert from sRGB to Rec. 709), and
1037         // I wouldn't really be surprised if most non-professional cameras do, too.
1038         // So we pick sRGB as the least evil here.
1039         inout_format.gamma_curve = GAMMA_sRGB;
1040
1041         unsigned num_inputs;
1042         if (deinterlace) {
1043                 deinterlace_effect = new movit::DeinterlaceEffect();
1044
1045                 // As per the comments in deinterlace_effect.h, we turn this off.
1046                 // The most likely interlaced input for us is either a camera
1047                 // (where it's fine to turn it off) or a laptop (where it _should_
1048                 // be turned off).
1049                 CHECK(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
1050
1051                 num_inputs = deinterlace_effect->num_inputs();
1052                 assert(num_inputs == FRAME_HISTORY_LENGTH);
1053         } else {
1054                 num_inputs = 1;
1055         }
1056
1057         if (pixel_format == bmusb::PixelFormat_8BitBGRA) {
1058                 for (unsigned i = 0; i < num_inputs; ++i) {
1059                         // We upload our textures ourselves, and Movit swaps
1060                         // R and B in the shader if we specify BGRA, so lie and say RGBA.
1061                         rgba_inputs.push_back(new sRGBSwitchingFlatInput(inout_format, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, global_flags.width, global_flags.height));
1062                         chain->add_input(rgba_inputs.back());
1063                 }
1064
1065                 if (deinterlace) {
1066                         vector<Effect *> reverse_inputs(rgba_inputs.rbegin(), rgba_inputs.rend());
1067                         chain->add_effect(deinterlace_effect, reverse_inputs);
1068                 }
1069         } else {
1070                 assert(pixel_format == bmusb::PixelFormat_8BitYCbCr ||
1071                        pixel_format == bmusb::PixelFormat_10BitYCbCr ||
1072                        pixel_format == bmusb::PixelFormat_8BitYCbCrPlanar);
1073
1074                 // Most of these settings will be overridden later if using PixelFormat_8BitYCbCrPlanar.
1075                 input_ycbcr_format.chroma_subsampling_x = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? 1 : 2;
1076                 input_ycbcr_format.chroma_subsampling_y = 1;
1077                 input_ycbcr_format.num_levels = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? 1024 : 256;
1078                 input_ycbcr_format.cb_x_position = 0.0;
1079                 input_ycbcr_format.cr_x_position = 0.0;
1080                 input_ycbcr_format.cb_y_position = 0.5;
1081                 input_ycbcr_format.cr_y_position = 0.5;
1082                 input_ycbcr_format.luma_coefficients = YCBCR_REC_709;  // Will be overridden later even if not planar.
1083                 input_ycbcr_format.full_range = false;  // Will be overridden later even if not planar.
1084
1085                 for (unsigned i = 0; i < num_inputs; ++i) {
1086                         // When using 10-bit input, we're converting to interleaved through v210Converter.
1087                         YCbCrInputSplitting splitting;
1088                         if (pixel_format == bmusb::PixelFormat_10BitYCbCr) {
1089                                 splitting = YCBCR_INPUT_INTERLEAVED;
1090                         } else if (pixel_format == bmusb::PixelFormat_8BitYCbCr) {
1091                                 splitting = YCBCR_INPUT_SPLIT_Y_AND_CBCR;
1092                         } else {
1093                                 splitting = YCBCR_INPUT_PLANAR;
1094                         }
1095                         if (override_bounce) {
1096                                 ycbcr_inputs.push_back(new NonBouncingYCbCrInput(inout_format, input_ycbcr_format, global_flags.width, global_flags.height, splitting));
1097                         } else {
1098                                 ycbcr_inputs.push_back(new YCbCrInput(inout_format, input_ycbcr_format, global_flags.width, global_flags.height, splitting));
1099                         }
1100                         chain->add_input(ycbcr_inputs.back());
1101                 }
1102
1103                 if (deinterlace) {
1104                         vector<Effect *> reverse_inputs(ycbcr_inputs.rbegin(), ycbcr_inputs.rend());
1105                         chain->add_effect(deinterlace_effect, reverse_inputs);
1106                 }
1107         }
1108 }
1109
1110 bool LiveInputWrapper::connect_signal(int signal_num)
1111 {
1112         if (!user_connectable) {
1113                 return false;
1114         }
1115
1116         if (global_mixer == nullptr) {
1117                 // No data yet.
1118                 return true;
1119         }
1120
1121         signal_num = theme->map_signal(signal_num);
1122         connect_signal_raw(signal_num, *theme->input_state);
1123         return true;
1124 }
1125
1126 void LiveInputWrapper::connect_signal_raw(int signal_num, const InputState &input_state)
1127 {
1128         BufferedFrame first_frame = input_state.buffered_frames[signal_num][0];
1129         if (first_frame.frame == nullptr) {
1130                 // No data yet.
1131                 return;
1132         }
1133         unsigned width, height;
1134         {
1135                 const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)first_frame.frame->userdata;
1136                 width = userdata->last_width[first_frame.field_number];
1137                 height = userdata->last_height[first_frame.field_number];
1138                 if (userdata->last_interlaced) {
1139                         height *= 2;
1140                 }
1141         }
1142
1143         movit::YCbCrLumaCoefficients ycbcr_coefficients = input_state.ycbcr_coefficients[signal_num];
1144         bool full_range = input_state.full_range[signal_num];
1145
1146         if (input_state.ycbcr_coefficients_auto[signal_num]) {
1147                 full_range = false;
1148
1149                 // The Blackmagic driver docs claim that the device outputs Y'CbCr
1150                 // according to Rec. 601, but this seems to indicate the subsampling
1151                 // positions only, as they publish Y'CbCr → RGB formulas that are
1152                 // different for HD and SD (corresponding to Rec. 709 and 601, respectively),
1153                 // and a Lenovo X1 gen 3 I used to test definitely outputs Rec. 709
1154                 // (at least up to rounding error). Other devices seem to use Rec. 601
1155                 // even on HD resolutions. Nevertheless, Rec. 709 _is_ the right choice
1156                 // for HD, so we default to that if the user hasn't set anything.
1157                 if (height >= 720) {
1158                         ycbcr_coefficients = YCBCR_REC_709;
1159                 } else {
1160                         ycbcr_coefficients = YCBCR_REC_601;
1161                 }
1162         }
1163
1164         // This is a global, but it doesn't really matter.
1165         input_ycbcr_format.luma_coefficients = ycbcr_coefficients;
1166         input_ycbcr_format.full_range = full_range;
1167
1168         BufferedFrame last_good_frame = first_frame;
1169         for (unsigned i = 0; i < max(ycbcr_inputs.size(), rgba_inputs.size()); ++i) {
1170                 BufferedFrame frame = input_state.buffered_frames[signal_num][i];
1171                 if (frame.frame == nullptr) {
1172                         // Not enough data; reuse last frame (well, field).
1173                         // This is suboptimal, but we have nothing better.
1174                         frame = last_good_frame;
1175                 }
1176                 const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
1177
1178                 unsigned this_width = userdata->last_width[frame.field_number];
1179                 unsigned this_height = userdata->last_height[frame.field_number];
1180                 if (this_width != width || this_height != height) {
1181                         // Resolution changed; reuse last frame/field.
1182                         frame = last_good_frame;
1183                         userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
1184                 }
1185
1186                 assert(userdata->pixel_format == pixel_format);
1187                 switch (pixel_format) {
1188                 case bmusb::PixelFormat_8BitYCbCr:
1189                         ycbcr_inputs[i]->set_texture_num(0, userdata->tex_y[frame.field_number]);
1190                         ycbcr_inputs[i]->set_texture_num(1, userdata->tex_cbcr[frame.field_number]);
1191                         ycbcr_inputs[i]->change_ycbcr_format(input_ycbcr_format);
1192                         ycbcr_inputs[i]->set_width(width);
1193                         ycbcr_inputs[i]->set_height(height);
1194                         break;
1195                 case bmusb::PixelFormat_8BitYCbCrPlanar:
1196                         ycbcr_inputs[i]->set_texture_num(0, userdata->tex_y[frame.field_number]);
1197                         ycbcr_inputs[i]->set_texture_num(1, userdata->tex_cb[frame.field_number]);
1198                         ycbcr_inputs[i]->set_texture_num(2, userdata->tex_cr[frame.field_number]);
1199                         ycbcr_inputs[i]->change_ycbcr_format(userdata->ycbcr_format);
1200                         ycbcr_inputs[i]->set_width(width);
1201                         ycbcr_inputs[i]->set_height(height);
1202                         break;
1203                 case bmusb::PixelFormat_10BitYCbCr:
1204                         ycbcr_inputs[i]->set_texture_num(0, userdata->tex_444[frame.field_number]);
1205                         ycbcr_inputs[i]->change_ycbcr_format(input_ycbcr_format);
1206                         ycbcr_inputs[i]->set_width(width);
1207                         ycbcr_inputs[i]->set_height(height);
1208                         break;
1209                 case bmusb::PixelFormat_8BitBGRA:
1210                         rgba_inputs[i]->set_texture_num(userdata->tex_rgba[frame.field_number]);
1211                         rgba_inputs[i]->set_width(width);
1212                         rgba_inputs[i]->set_height(height);
1213                         break;
1214                 default:
1215                         assert(false);
1216                 }
1217
1218                 last_good_frame = frame;
1219         }
1220
1221         if (deinterlace) {
1222                 BufferedFrame frame = input_state.buffered_frames[signal_num][0];
1223                 CHECK(deinterlace_effect->set_int("current_field_position", frame.field_number));
1224         }
1225 }
1226
1227 namespace {
1228
1229 int call_num_channels(lua_State *L)
1230 {
1231         lua_getglobal(L, "num_channels");
1232
1233         if (lua_pcall(L, 0, 1, 0) != 0) {
1234                 fprintf(stderr, "error running function `num_channels': %s\n", lua_tostring(L, -1));
1235                 fprintf(stderr, "Try Nageru.set_num_channels(...) at the start of the script instead.\n");
1236                 abort();
1237         }
1238
1239         int num_channels = luaL_checknumber(L, 1);
1240         lua_pop(L, 1);
1241         assert(lua_gettop(L) == 0);
1242         return num_channels;
1243 }
1244
1245 }  // namespace
1246
1247 int Nageru_set_channel_name(lua_State *L)
1248 {
1249         // NOTE: m is already locked.
1250         Theme *theme = get_theme_updata(L);
1251         unsigned channel = luaL_checknumber(L, 1);
1252         const string text = checkstdstring(L, 2);
1253         theme->channel_names[channel] = text;
1254         lua_pop(L, 2);
1255         return 0;
1256 }
1257
1258 int Nageru_set_num_channels(lua_State *L)
1259 {
1260         // NOTE: m is already locked.
1261         Theme *theme = get_theme_updata(L);
1262         if (theme->startup_finished) {
1263                 luaL_error(L, "set_num_channels() can only be called at startup.");
1264         }
1265         theme->num_channels = luaL_checknumber(L, 1);
1266         lua_pop(L, 1);
1267         return 0;
1268 }
1269
1270 int Nageru_set_channel_signal(lua_State *L)
1271 {
1272         // NOTE: m is already locked.
1273         Theme *theme = get_theme_updata(L);
1274         if (theme->startup_finished) {
1275                 luaL_error(L, "set_channel_signal() can only be called at startup.");
1276         }
1277         unsigned channel = luaL_checknumber(L, 1);
1278         int signal = luaL_checknumber(L, 2);
1279         theme->channel_signals[channel] = signal;
1280         lua_pop(L, 2);
1281         return 0;
1282 }
1283
1284 int Nageru_set_supports_wb(lua_State *L)
1285 {
1286         // NOTE: m is already locked.
1287         Theme *theme = get_theme_updata(L);
1288         if (theme->startup_finished) {
1289                 luaL_error(L, "set_supports_wb() can only be called at startup.");
1290         }
1291         unsigned channel = luaL_checknumber(L, 1);
1292         bool supports_wb = checkbool(L, 2);
1293         theme->channel_supports_wb[channel] = supports_wb;
1294         lua_pop(L, 2);
1295         return 0;
1296 }
1297
1298 Theme::Theme(const string &filename, const vector<string> &search_dirs, ResourcePool *resource_pool, unsigned num_cards)
1299         : resource_pool(resource_pool), num_cards(num_cards), signal_to_card_mapping(global_flags.default_stream_mapping)
1300 {
1301         // Defaults.
1302         channel_names[0] = "Live";
1303         channel_names[1] = "Preview";
1304
1305         L = luaL_newstate();
1306         luaL_openlibs(L);
1307
1308         // Search through all directories until we find a file that will load
1309         // (as in, does not return LUA_ERRFILE); then run it. We store load errors
1310         // from all the attempts, and show them once we know we can't find any of them.
1311         lua_settop(L, 0);
1312         vector<string> errors;
1313         bool success = false;
1314
1315         vector<string> real_search_dirs;
1316         if (!filename.empty() && filename[0] == '/') {
1317                 real_search_dirs.push_back("");
1318         } else {
1319                 real_search_dirs = search_dirs;
1320         }
1321
1322         string path;
1323         int theme_code_ref;
1324         for (const string &dir : real_search_dirs) {
1325                 if (dir.empty()) {
1326                         path = filename;
1327                 } else {
1328                         path = dir + "/" + filename;
1329                 }
1330                 int err = luaL_loadfile(L, path.c_str());
1331                 if (err == 0) {
1332                         // Save the theme for when we're actually going to run it
1333                         // (we need to set up the right environment below first,
1334                         // and we couldn't do that before, because we didn't know the
1335                         // path to put in Nageru.THEME_PATH).
1336                         theme_code_ref = luaL_ref(L, LUA_REGISTRYINDEX);
1337                         assert(lua_gettop(L) == 0);
1338
1339                         success = true;
1340                         break;
1341                 }
1342                 errors.push_back(lua_tostring(L, -1));
1343                 lua_pop(L, 1);
1344                 if (err != LUA_ERRFILE) {
1345                         // The file actually loaded, but failed to parse somehow. Abort; don't try the next one.
1346                         break;
1347                 }
1348         }
1349
1350         if (!success) {
1351                 for (const string &error : errors) {
1352                         fprintf(stderr, "%s\n", error.c_str());
1353                 }
1354                 abort();
1355         }
1356         assert(lua_gettop(L) == 0);
1357
1358         // Make sure the path exposed to the theme (as Nageru.THEME_PATH;
1359         // can be useful for locating files when talking to CEF) is absolute.
1360         // In a sense, it would be nice if realpath() had a mode not to
1361         // resolve symlinks, but it doesn't, so we only call it if we don't
1362         // already have an absolute path (which may leave ../ elements etc.).
1363         if (path[0] == '/') {
1364                 theme_path = path;
1365         } else {
1366                 char *absolute_theme_path = realpath(path.c_str(), nullptr);
1367                 theme_path = absolute_theme_path;
1368                 free(absolute_theme_path);
1369         }
1370
1371         // Set up the API we provide.
1372         register_globals();
1373         register_class("Scene", Scene_funcs);
1374         register_class("Block", Block_funcs);
1375         register_class("EffectBlueprint", EffectBlueprint_funcs);
1376         register_class("EffectChain", EffectChain_funcs);
1377         register_class("LiveInputWrapper", LiveInputWrapper_funcs);
1378         register_class("ImageInput", ImageInput_funcs);
1379         register_class("VideoInput", VideoInput_funcs);
1380         register_class("HTMLInput", HTMLInput_funcs);
1381         register_class("IdentityEffect", IdentityEffect_funcs, IDENTITY_EFFECT);
1382         register_class("WhiteBalanceEffect", WhiteBalanceEffect_funcs, WHITE_BALANCE_EFFECT);
1383         register_class("ResampleEffect", ResampleEffect_funcs, RESAMPLE_EFFECT);
1384         register_class("PaddingEffect", PaddingEffect_funcs, PADDING_EFFECT);
1385         register_class("IntegralPaddingEffect", IntegralPaddingEffect_funcs, INTEGRAL_PADDING_EFFECT);
1386         register_class("OverlayEffect", OverlayEffect_funcs, OVERLAY_EFFECT);
1387         register_class("ResizeEffect", ResizeEffect_funcs, RESIZE_EFFECT);
1388         register_class("MultiplyEffect", MultiplyEffect_funcs, MULTIPLY_EFFECT);
1389         register_class("MixEffect", MixEffect_funcs, MIX_EFFECT);
1390         register_class("LiftGammaGainEffect", LiftGammaGainEffect_funcs, LIFT_GAMMA_GAIN_EFFECT);
1391         register_class("InputStateInfo", InputStateInfo_funcs);
1392         register_class("ThemeMenu", ThemeMenu_funcs);
1393
1394         // Now actually run the theme to get everything set up.
1395         lua_rawgeti(L, LUA_REGISTRYINDEX, theme_code_ref);
1396         luaL_unref(L, LUA_REGISTRYINDEX, theme_code_ref);
1397         if (lua_pcall(L, 0, 0, 0)) {
1398                 fprintf(stderr, "Error when running %s: %s\n", path.c_str(), lua_tostring(L, -1));
1399                 abort();
1400         }
1401         assert(lua_gettop(L) == 0);
1402
1403         if (num_channels == -1) {
1404                 // Ask it for the number of channels.
1405                 num_channels = call_num_channels(L);
1406         }
1407         startup_finished = true;
1408 }
1409
1410 Theme::~Theme()
1411 {
1412         theme_menu.reset();
1413         lua_close(L);
1414 }
1415
1416 void Theme::register_globals()
1417 {
1418         // Set Nageru.VIDEO_FORMAT_BGRA = bmusb::PixelFormat_8BitBGRA, etc.
1419         const vector<pair<string, int>> num_constants = {
1420                 { "VIDEO_FORMAT_BGRA", bmusb::PixelFormat_8BitBGRA },
1421                 { "VIDEO_FORMAT_YCBCR", bmusb::PixelFormat_8BitYCbCrPlanar },
1422                 { "CHECKABLE", MenuEntry::CHECKABLE },
1423                 { "CHECKED", MenuEntry::CHECKED },
1424         };
1425         const vector<pair<string, string>> str_constants = {
1426                 { "THEME_PATH", theme_path },
1427         };
1428
1429         lua_newtable(L);  // t = {}
1430
1431         for (const pair<string, int> &constant : num_constants) {
1432                 lua_pushstring(L, constant.first.c_str());
1433                 lua_pushinteger(L, constant.second);
1434                 lua_settable(L, 1);  // t[key] = value
1435         }
1436         for (const pair<string, string> &constant : str_constants) {
1437                 lua_pushstring(L, constant.first.c_str());
1438                 lua_pushstring(L, constant.second.c_str());
1439                 lua_settable(L, 1);  // t[key] = value
1440         }
1441
1442         const luaL_Reg Nageru_funcs[] = {
1443                 { "set_channel_name", Nageru_set_channel_name },
1444                 { "set_num_channels", Nageru_set_num_channels },
1445                 { "set_channel_signal", Nageru_set_channel_signal },
1446                 { "set_supports_wb", Nageru_set_supports_wb },
1447                 { NULL, NULL }
1448         };
1449         lua_pushlightuserdata(L, this);
1450         luaL_setfuncs(L, Nageru_funcs, 1);        // for (name,f in funcs) { mt[name] = f, with upvalue {theme} }
1451
1452         lua_setglobal(L, "Nageru");  // Nageru = t
1453         assert(lua_gettop(L) == 0);
1454 }
1455
1456 void Theme::register_class(const char *class_name, const luaL_Reg *funcs, EffectType effect_type)
1457 {
1458         assert(lua_gettop(L) == 0);
1459         luaL_newmetatable(L, class_name);  // mt = {}
1460         lua_pushlightuserdata(L, this);
1461         luaL_setfuncs(L, funcs, 1);        // for (name,f in funcs) { mt[name] = f, with upvalue {theme} }
1462         lua_pushvalue(L, -1);
1463         lua_setfield(L, -2, "__index");    // mt.__index = mt
1464         if (effect_type != NO_EFFECT_TYPE) {
1465                 lua_pushnumber(L, effect_type);
1466                 lua_setfield(L, -2, "__effect_type_id");  // mt.__effect_type_id = effect_type
1467         }
1468         lua_setglobal(L, class_name);      // ClassName = mt
1469         assert(lua_gettop(L) == 0);
1470 }
1471
1472 Theme::Chain Theme::get_chain_from_effect_chain(EffectChain *effect_chain, unsigned num, const InputState &input_state)
1473 {
1474         if (!lua_isfunction(L, -1)) {
1475                 fprintf(stderr, "Argument #-1 should be a function\n");
1476                 abort();
1477         }
1478         lua_pushvalue(L, -1);
1479         shared_ptr<LuaRefWithDeleter> funcref(new LuaRefWithDeleter(&m, L, luaL_ref(L, LUA_REGISTRYINDEX)));
1480         lua_pop(L, 2);
1481
1482         Chain chain;
1483         chain.chain = effect_chain;
1484         chain.setup_chain = [this, funcref, input_state, effect_chain]{
1485                 lock_guard<mutex> lock(m);
1486
1487                 assert(this->input_state == nullptr);
1488                 this->input_state = &input_state;
1489
1490                 // Set up state, including connecting signals.
1491                 lua_rawgeti(L, LUA_REGISTRYINDEX, funcref->get());
1492                 if (lua_pcall(L, 0, 0, 0) != 0) {
1493                         fprintf(stderr, "error running chain setup callback: %s\n", lua_tostring(L, -1));
1494                         abort();
1495                 }
1496                 assert(lua_gettop(L) == 0);
1497
1498                 // The theme can't (or at least shouldn't!) call connect_signal() on
1499                 // each FFmpeg or CEF input, so we'll do it here.
1500                 if (video_signal_connections.count(effect_chain)) {
1501                         for (const VideoSignalConnection &conn : video_signal_connections[effect_chain]) {
1502                                 conn.wrapper->connect_signal_raw(conn.source->get_card_index(), input_state);
1503                         }
1504                 }
1505 #ifdef HAVE_CEF
1506                 if (html_signal_connections.count(effect_chain)) {
1507                         for (const CEFSignalConnection &conn : html_signal_connections[effect_chain]) {
1508                                 conn.wrapper->connect_signal_raw(conn.source->get_card_index(), input_state);
1509                         }
1510                 }
1511 #endif
1512
1513                 this->input_state = nullptr;
1514         };
1515         return chain;
1516 }
1517
1518 Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned height, const InputState &input_state)
1519 {
1520         const char *func_name = "get_scene";  // For error reporting.
1521         Chain chain;
1522
1523         lock_guard<mutex> lock(m);
1524         assert(lua_gettop(L) == 0);
1525         lua_getglobal(L, "get_scene");  /* function to be called */
1526         if (lua_isnil(L, -1)) {
1527                 // Try the pre-1.9.0 name for compatibility.
1528                 lua_pop(L, 1);
1529                 lua_getglobal(L, "get_chain");
1530                 func_name = "get_chain";
1531         }
1532         lua_pushnumber(L, num);
1533         lua_pushnumber(L, t);
1534         lua_pushnumber(L, width);
1535         lua_pushnumber(L, height);
1536         wrap_lua_object<InputStateInfo>(L, "InputStateInfo", input_state);
1537
1538         if (lua_pcall(L, 5, LUA_MULTRET, 0) != 0) {
1539                 fprintf(stderr, "error running function “%s”: %s\n", func_name, lua_tostring(L, -1));
1540                 abort();
1541         }
1542
1543         if (luaL_testudata(L, -1, "Scene") != nullptr) {
1544                 if (lua_gettop(L) != 1) {
1545                         luaL_error(L, "%s() for chain number %d returned an Scene, but also other items", func_name);
1546                 }
1547                 Scene *auto_effect_chain = (Scene *)luaL_testudata(L, -1, "Scene");
1548                 auto chain_and_setup = auto_effect_chain->get_chain(this, L, num, input_state);
1549                 chain.chain = chain_and_setup.first;
1550                 chain.setup_chain = move(chain_and_setup.second);
1551         } else if (luaL_testudata(L, -2, "EffectChain") != nullptr) {
1552                 // Old-style (pre-Nageru 1.9.0) return of a single chain and prepare function.
1553                 if (lua_gettop(L) != 2) {
1554                         luaL_error(L, "%s() for chain number %d returned an EffectChain, but needs to also return a prepare function (or use Scene)", func_name);
1555                 }
1556                 EffectChain *effect_chain = (EffectChain *)luaL_testudata(L, -2, "EffectChain");
1557                 chain = get_chain_from_effect_chain(effect_chain, num, input_state);
1558         } else {
1559                 luaL_error(L, "%s() for chain number %d did not return an EffectChain or Scene\n", func_name, num);
1560         }
1561         assert(lua_gettop(L) == 0);
1562
1563         // TODO: Can we do better, e.g. by running setup_chain() and seeing what it references?
1564         // Actually, setup_chain does maybe hold all the references we need now anyway?
1565         chain.input_frames.reserve(num_cards * FRAME_HISTORY_LENGTH);
1566         for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
1567                 for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) {
1568                         chain.input_frames.push_back(input_state.buffered_frames[card_index][frame_num].frame);
1569                 }
1570         }
1571
1572         return chain;
1573 }
1574
1575 string Theme::get_channel_name(unsigned channel)
1576 {
1577         lock_guard<mutex> lock(m);
1578
1579         // We never ask the legacy channel_name() about live and preview.
1580         // The defaults are set in our constructor.
1581         if (channel == 0 || channel == 1) {
1582                 return channel_names[channel];
1583         }
1584
1585         lua_getglobal(L, "channel_name");
1586         if (lua_isnil(L, -1)) {
1587                 lua_pop(L, 1);
1588                 if (channel_names.count(channel)) {
1589                         return channel_names[channel];
1590                 } else {
1591                         return "(no title)";
1592                 }
1593         }
1594
1595         lua_pushnumber(L, channel);
1596         if (lua_pcall(L, 1, 1, 0) != 0) {
1597                 fprintf(stderr, "error running function `channel_name': %s\n", lua_tostring(L, -1));
1598                 abort();
1599         }
1600         const char *ret = lua_tostring(L, -1);
1601         if (ret == nullptr) {
1602                 fprintf(stderr, "function `channel_name' returned nil for channel %d\n", channel);
1603                 fprintf(stderr, "Try Nageru.set_channel_name(channel, name) at the start of the script instead.\n");
1604                 abort();
1605         }
1606
1607         string retstr = ret;
1608         lua_pop(L, 1);
1609         assert(lua_gettop(L) == 0);
1610         return retstr;
1611 }
1612
1613 int Theme::get_channel_signal(unsigned channel)
1614 {
1615         lock_guard<mutex> lock(m);
1616         lua_getglobal(L, "channel_signal");
1617         if (lua_isnil(L, -1)) {
1618                 lua_pop(L, 1);
1619                 if (channel_signals.count(channel)) {
1620                         return channel_signals[channel];
1621                 } else {
1622                         return -1;
1623                 }
1624         }
1625
1626         lua_pushnumber(L, channel);
1627         if (lua_pcall(L, 1, 1, 0) != 0) {
1628                 fprintf(stderr, "error running function `channel_signal': %s\n", lua_tostring(L, -1));
1629                 fprintf(stderr, "Try Nageru.set_channel_signal(channel, signal) at the start of the script instead.\n");
1630                 abort();
1631         }
1632
1633         int ret = luaL_checknumber(L, 1);
1634         lua_pop(L, 1);
1635         assert(lua_gettop(L) == 0);
1636         return ret;
1637 }
1638
1639 std::string Theme::get_channel_color(unsigned channel)
1640 {
1641         lock_guard<mutex> lock(m);
1642         lua_getglobal(L, "channel_color");
1643         lua_pushnumber(L, channel);
1644         if (lua_pcall(L, 1, 1, 0) != 0) {
1645                 fprintf(stderr, "error running function `channel_color': %s\n", lua_tostring(L, -1));
1646                 abort();
1647         }
1648
1649         const char *ret = lua_tostring(L, -1);
1650         if (ret == nullptr) {
1651                 fprintf(stderr, "function `channel_color' returned nil for channel %d\n", channel);
1652                 abort();
1653         }
1654
1655         string retstr = ret;
1656         lua_pop(L, 1);
1657         assert(lua_gettop(L) == 0);
1658         return retstr;
1659 }
1660
1661 bool Theme::get_supports_set_wb(unsigned channel)
1662 {
1663         lock_guard<mutex> lock(m);
1664         lua_getglobal(L, "supports_set_wb");
1665         if (lua_isnil(L, -1)) {
1666                 lua_pop(L, 1);
1667                 if (channel_supports_wb.count(channel)) {
1668                         return channel_supports_wb[channel];
1669                 } else {
1670                         return false;
1671                 }
1672         }
1673
1674         lua_pushnumber(L, channel);
1675         if (lua_pcall(L, 1, 1, 0) != 0) {
1676                 fprintf(stderr, "error running function `supports_set_wb': %s\n", lua_tostring(L, -1));
1677                 fprintf(stderr, "Try Nageru.set_supports_wb(channel, bool) at the start of the script instead.\n");
1678                 abort();
1679         }
1680
1681         bool ret = checkbool(L, -1);
1682         lua_pop(L, 1);
1683         assert(lua_gettop(L) == 0);
1684         return ret;
1685 }
1686
1687 void Theme::set_wb(unsigned channel, float r, float g, float b)
1688 {
1689         lock_guard<mutex> lock(m);
1690         if (channel_signals.count(channel)) {
1691                 white_balance_for_signal[channel_signals[channel]] = RGBTriplet{ r, g, b };
1692         }
1693
1694         call_lua_wb_callback(channel, r, g, b);
1695 }
1696
1697 void Theme::set_wb_for_signal(int signal, float r, float g, float b)
1698 {
1699         lock_guard<mutex> lock(m);
1700         white_balance_for_signal[signal] = RGBTriplet{ r, g, b };
1701
1702         for (const auto &channel_and_signal : channel_signals) {
1703                 if (channel_and_signal.second == signal) {
1704                         call_lua_wb_callback(channel_and_signal.first, r, g, b);
1705                 }
1706         }
1707 }
1708
1709 void Theme::call_lua_wb_callback(unsigned channel, float r, float g, float b)
1710 {
1711         lua_getglobal(L, "set_wb");
1712         if (lua_isnil(L, -1)) {
1713                 // The function doesn't exist, to just ignore. We've stored the white balance,
1714                 // and most likely, it will be picked up by auto white balance instead.
1715                 lua_pop(L, 1);
1716                 return;
1717         }
1718         lua_pushnumber(L, channel);
1719         lua_pushnumber(L, r);
1720         lua_pushnumber(L, g);
1721         lua_pushnumber(L, b);
1722         if (lua_pcall(L, 4, 0, 0) != 0) {
1723                 fprintf(stderr, "error running function `set_wb': %s\n", lua_tostring(L, -1));
1724                 abort();
1725         }
1726
1727         assert(lua_gettop(L) == 0);
1728 }
1729
1730 RGBTriplet Theme::get_white_balance_for_signal(int signal)
1731 {
1732         if (white_balance_for_signal.count(signal)) {
1733                 return white_balance_for_signal[signal];
1734         } else {
1735                 return RGBTriplet{ 1.0, 1.0, 1.0 };
1736         }
1737 }
1738
1739 vector<string> Theme::get_transition_names(float t)
1740 {
1741         lock_guard<mutex> lock(m);
1742         lua_getglobal(L, "get_transitions");
1743         lua_pushnumber(L, t);
1744         if (lua_pcall(L, 1, 1, 0) != 0) {
1745                 fprintf(stderr, "error running function `get_transitions': %s\n", lua_tostring(L, -1));
1746                 abort();
1747         }
1748
1749         vector<string> ret;
1750         lua_pushnil(L);
1751         while (lua_next(L, -2) != 0) {
1752                 ret.push_back(lua_tostring(L, -1));
1753                 lua_pop(L, 1);
1754         }
1755         lua_pop(L, 1);
1756         assert(lua_gettop(L) == 0);
1757         return ret;
1758 }
1759
1760 int Theme::map_signal(int signal_num)
1761 {
1762         // Negative numbers map to raw signals.
1763         if (signal_num < 0) {
1764                 return -1 - signal_num;
1765         }
1766
1767         lock_guard<mutex> lock(map_m);
1768         if (signal_to_card_mapping.count(signal_num)) {
1769                 return signal_to_card_mapping[signal_num];
1770         }
1771
1772         int card_index;
1773         if (global_flags.output_card != -1 && num_cards > 1) {
1774                 // Try to exclude the output card from the default card_index.
1775                 card_index = signal_num % (num_cards - 1);
1776                 if (card_index >= global_flags.output_card) {
1777                          ++card_index;
1778                 }
1779                 if (signal_num >= int(num_cards - 1)) {
1780                         print_warning(L, "Theme asked for input %d, but we only have %u input card(s) (card %d is busy with output).\n",
1781                                 signal_num, num_cards - 1, global_flags.output_card);
1782                         fprintf(stderr, "Mapping to card %d instead.\n", card_index);
1783                 }
1784         } else {
1785                 card_index = signal_num % num_cards;
1786                 if (signal_num >= int(num_cards)) {
1787                         print_warning(L, "Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards);
1788                         fprintf(stderr, "Mapping to card %d instead.\n", card_index);
1789                 }
1790         }
1791         signal_to_card_mapping[signal_num] = card_index;
1792         return card_index;
1793 }
1794
1795 void Theme::set_signal_mapping(int signal_num, int card_num)
1796 {
1797         lock_guard<mutex> lock(map_m);
1798         assert(card_num < int(num_cards));
1799         signal_to_card_mapping[signal_num] = card_num;
1800 }
1801
1802 void Theme::transition_clicked(int transition_num, float t)
1803 {
1804         lock_guard<mutex> lock(m);
1805         lua_getglobal(L, "transition_clicked");
1806         lua_pushnumber(L, transition_num);
1807         lua_pushnumber(L, t);
1808
1809         if (lua_pcall(L, 2, 0, 0) != 0) {
1810                 fprintf(stderr, "error running function `transition_clicked': %s\n", lua_tostring(L, -1));
1811                 abort();
1812         }
1813         assert(lua_gettop(L) == 0);
1814 }
1815
1816 void Theme::channel_clicked(int preview_num)
1817 {
1818         lock_guard<mutex> lock(m);
1819         lua_getglobal(L, "channel_clicked");
1820         lua_pushnumber(L, preview_num);
1821
1822         if (lua_pcall(L, 1, 0, 0) != 0) {
1823                 fprintf(stderr, "error running function `channel_clicked': %s\n", lua_tostring(L, -1));
1824                 abort();
1825         }
1826         assert(lua_gettop(L) == 0);
1827 }
1828
1829 template <class T>
1830 void destroy(T &ref)
1831 {
1832         ref.~T();
1833 }
1834
1835 Theme::MenuEntry::~MenuEntry()
1836 {
1837         if (is_submenu) {
1838                 destroy(submenu);
1839         } else {
1840                 luaL_unref(entry.L, LUA_REGISTRYINDEX, entry.lua_ref);
1841         }
1842 }
1843
1844 namespace {
1845
1846 vector<unique_ptr<Theme::MenuEntry>> create_recursive_theme_menu(lua_State *L);
1847
1848 unique_ptr<Theme::MenuEntry> create_theme_menu_entry(lua_State *L, int index)
1849 {
1850         unique_ptr<Theme::MenuEntry> entry;
1851
1852         lua_rawgeti(L, index, 1);
1853         const string text = checkstdstring(L, -1);
1854         lua_pop(L, 1);
1855
1856         unsigned flags = 0;
1857         if (lua_objlen(L, -1) > 2) {
1858                 lua_rawgeti(L, -1, 3);
1859                 flags = luaL_checknumber(L, -1);
1860                 lua_pop(L, 1);
1861         }
1862
1863         lua_rawgeti(L, index, 2);
1864         if (lua_istable(L, -1)) {
1865                 vector<unique_ptr<Theme::MenuEntry>> submenu = create_recursive_theme_menu(L);
1866                 entry.reset(new Theme::MenuEntry{ text, move(submenu) });
1867                 lua_pop(L, 1);
1868         } else {
1869                 luaL_checktype(L, -1, LUA_TFUNCTION);
1870                 int ref = luaL_ref(L, LUA_REGISTRYINDEX);
1871                 entry.reset(new Theme::MenuEntry{ text, L, ref, flags });
1872         }
1873         return entry;
1874 }
1875
1876 vector<unique_ptr<Theme::MenuEntry>> create_recursive_theme_menu(lua_State *L)
1877 {
1878         vector<unique_ptr<Theme::MenuEntry>> menu;
1879         size_t num_elements = lua_objlen(L, -1);
1880         for (size_t i = 1; i <= num_elements; ++i) {
1881                 lua_rawgeti(L, -1, i);
1882                 menu.emplace_back(create_theme_menu_entry(L, -1));
1883                 lua_pop(L, 1);
1884         }
1885         return menu;
1886 }
1887
1888 }  // namespace
1889
1890 int Theme::set_theme_menu(lua_State *L)
1891 {
1892         theme_menu.reset();
1893
1894         vector<unique_ptr<MenuEntry>> root_menu;
1895         int num_elements = lua_gettop(L);
1896         for (int i = 1; i <= num_elements; ++i) {
1897                 root_menu.emplace_back(create_theme_menu_entry(L, i));
1898         }
1899         theme_menu.reset(new MenuEntry("", move(root_menu)));
1900
1901         lua_pop(L, num_elements);
1902         assert(lua_gettop(L) == 0);
1903
1904         if (theme_menu_callback != nullptr) {
1905                 theme_menu_callback();
1906         }
1907
1908         return 0;
1909 }
1910
1911 void Theme::theme_menu_entry_clicked(int lua_ref)
1912 {
1913         lock_guard<mutex> lock(m);
1914         lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
1915         if (lua_pcall(L, 0, 0, 0) != 0) {
1916                 fprintf(stderr, "error running menu callback: %s\n", lua_tostring(L, -1));
1917                 abort();
1918         }
1919 }
1920
1921 string Theme::format_status_line(const string &disk_space_left_text, double file_length_seconds)
1922 {
1923         lock_guard<mutex> lock(m);
1924         lua_getglobal(L, "format_status_line");
1925         if (lua_isnil(L, -1)) {
1926                 lua_pop(L, 1);
1927                 return disk_space_left_text;
1928         }
1929
1930         lua_pushstring(L, disk_space_left_text.c_str());
1931         lua_pushnumber(L, file_length_seconds);
1932         if (lua_pcall(L, 2, 1, 0) != 0) {
1933                 fprintf(stderr, "error running function format_status_line(): %s\n", lua_tostring(L, -1));
1934                 abort();
1935         }
1936         string text = checkstdstring(L, 1);
1937         lua_pop(L, 1);
1938         assert(lua_gettop(L) == 0);
1939         return text;
1940 }