]> git.sesse.net Git - nageru/blob - theme.cpp
Make a metadata-only version of InputState to be sent to Lua, in order to not be...
[nageru] / theme.cpp
1 #include "theme.h"
2
3 #include <assert.h>
4 #include <lauxlib.h>
5 #include <lua.h>
6 #include <lualib.h>
7 #include <movit/effect.h>
8 #include <movit/effect_chain.h>
9 #include <movit/image_format.h>
10 #include <movit/mix_effect.h>
11 #include <movit/overlay_effect.h>
12 #include <movit/padding_effect.h>
13 #include <movit/resample_effect.h>
14 #include <movit/resize_effect.h>
15 #include <movit/white_balance_effect.h>
16 #include <movit/ycbcr.h>
17 #include <movit/ycbcr_input.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <cstddef>
21 #include <new>
22 #include <utility>
23 #include <memory>
24
25 #include "defs.h"
26 #include "image_input.h"
27 #include "mixer.h"
28
29 namespace movit {
30 class ResourcePool;
31 }  // namespace movit
32
33 using namespace std;
34 using namespace movit;
35
36 extern Mixer *global_mixer;
37
38 namespace {
39
40 // Contains basically the same data as InputState, but does not hold on to
41 // a reference to the frames. This is important so that we can release them
42 // without having to wait for Lua's GC.
43 struct InputStateInfo {
44         InputStateInfo(const InputState& input_state);
45
46         unsigned last_width[MAX_CARDS], last_height[MAX_CARDS];
47 };
48
49 InputStateInfo::InputStateInfo(const InputState &input_state)
50 {
51         for (unsigned signal_num = 0; signal_num < MAX_CARDS; ++signal_num) {
52                 BufferedFrame frame = input_state.buffered_frames[signal_num][0];
53                 if (frame.frame == nullptr) {
54                         last_width[signal_num] = last_height[signal_num] = 0;
55                         continue;
56                 }
57                 const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
58                 last_width[signal_num] = userdata->last_width[frame.field_number];
59                 last_height[signal_num] = userdata->last_height[frame.field_number];
60         }
61 }
62
63 class LuaRefWithDeleter {
64 public:
65         LuaRefWithDeleter(mutex *m, lua_State *L, int ref) : m(m), L(L), ref(ref) {}
66         ~LuaRefWithDeleter() {
67                 unique_lock<mutex> lock(*m);
68                 luaL_unref(L, LUA_REGISTRYINDEX, ref);
69         }
70         int get() const { return ref; }
71
72 private:
73         LuaRefWithDeleter(const LuaRefWithDeleter &) = delete;
74
75         mutex *m;
76         lua_State *L;
77         int ref;
78 };
79
80 template<class T, class... Args>
81 int wrap_lua_object(lua_State* L, const char *class_name, Args&&... args)
82 {
83         // Construct the C++ object and put it on the stack.
84         void *mem = lua_newuserdata(L, sizeof(T));
85         new(mem) T(std::forward<Args>(args)...);
86
87         // Look up the metatable named <class_name>, and set it on the new object.
88         luaL_getmetatable(L, class_name);
89         lua_setmetatable(L, -2);
90
91         return 1;
92 }
93
94 Theme *get_theme_updata(lua_State* L)
95 {       
96         luaL_checktype(L, lua_upvalueindex(1), LUA_TLIGHTUSERDATA);
97         return (Theme *)lua_touserdata(L, lua_upvalueindex(1));
98 }
99
100 Effect *get_effect(lua_State *L, int idx)
101 {
102         if (luaL_testudata(L, idx, "WhiteBalanceEffect") ||
103             luaL_testudata(L, idx, "ResampleEffect") ||
104             luaL_testudata(L, idx, "PaddingEffect") ||
105             luaL_testudata(L, idx, "IntegralPaddingEffect") ||
106             luaL_testudata(L, idx, "OverlayEffect") ||
107             luaL_testudata(L, idx, "ResizeEffect") ||
108             luaL_testudata(L, idx, "MixEffect") ||
109             luaL_testudata(L, idx, "ImageInput")) {
110                 return (Effect *)lua_touserdata(L, idx);
111         }
112         luaL_error(L, "Error: Index #%d was not an Effect type\n", idx);
113         return nullptr;
114 }
115
116 InputStateInfo *get_input_state_info(lua_State *L, int idx)
117 {
118         if (luaL_testudata(L, idx, "InputStateInfo")) {
119                 return (InputStateInfo *)lua_touserdata(L, idx);
120         }
121         luaL_error(L, "Error: Index #%d was not InputStateInfo\n", idx);
122         return nullptr;
123 }
124
125 bool checkbool(lua_State* L, int idx)
126 {
127         luaL_checktype(L, idx, LUA_TBOOLEAN);
128         return lua_toboolean(L, idx);
129 }
130
131 std::string checkstdstring(lua_State *L, int index)
132 {
133         size_t len;
134         const char* cstr = lua_tolstring(L, index, &len);
135         return std::string(cstr, len);
136 }
137
138 int EffectChain_new(lua_State* L)
139 {
140         assert(lua_gettop(L) == 2);
141         int aspect_w = luaL_checknumber(L, 1);
142         int aspect_h = luaL_checknumber(L, 2);
143
144         return wrap_lua_object<EffectChain>(L, "EffectChain", aspect_w, aspect_h);
145 }
146
147 int EffectChain_add_live_input(lua_State* L)
148 {
149         assert(lua_gettop(L) == 2);
150         Theme *theme = get_theme_updata(L);
151         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
152         bool override_bounce = checkbool(L, 2);
153         return wrap_lua_object<LiveInputWrapper>(L, "LiveInputWrapper", theme, chain, override_bounce);
154 }
155
156 int EffectChain_add_effect(lua_State* L)
157 {
158         assert(lua_gettop(L) >= 2);
159         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
160
161         // TODO: Better error reporting.
162         Effect *effect = get_effect(L, 2);
163         if (lua_gettop(L) == 2) {
164                 if (effect->num_inputs() == 0) {
165                         chain->add_input((Input *)effect);
166                 } else {
167                         chain->add_effect(effect);
168                 }
169         } else {
170                 vector<Effect *> inputs;
171                 for (int idx = 3; idx <= lua_gettop(L); ++idx) {
172                         if (luaL_testudata(L, idx, "LiveInputWrapper")) {
173                                 LiveInputWrapper *input = (LiveInputWrapper *)lua_touserdata(L, idx);
174                                 inputs.push_back(input->get_input());
175                         } else {
176                                 inputs.push_back(get_effect(L, idx));
177                         }
178                 }
179                 chain->add_effect(effect, inputs);
180         }
181
182         lua_settop(L, 2);  // Return the effect itself.
183
184         // Make sure Lua doesn't garbage-collect it away.
185         lua_pushvalue(L, -1);
186         luaL_ref(L, LUA_REGISTRYINDEX);  // TODO: leak?
187
188         return 1;
189 }
190
191 int EffectChain_finalize(lua_State* L)
192 {
193         assert(lua_gettop(L) == 2);
194         EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
195         bool is_main_chain = checkbool(L, 2);
196
197         // Add outputs as needed.
198         // NOTE: If you change any details about the output format, you will need to
199         // also update what's given to the muxer (HTTPD::Mux constructor) and
200         // what's put in the H.264 stream (sps_rbsp()).
201         ImageFormat inout_format;
202         inout_format.color_space = COLORSPACE_REC_709;
203
204         // Output gamma is tricky. We should output Rec. 709 for TV, except that
205         // we expect to run with web players and others that don't really care and
206         // just output with no conversion. So that means we'll need to output sRGB,
207         // even though H.264 has no setting for that (we use “unspecified”).
208         inout_format.gamma_curve = GAMMA_sRGB;
209
210         if (is_main_chain) {
211                 YCbCrFormat output_ycbcr_format;
212                 // We actually output 4:2:0 in the end, but chroma subsampling
213                 // happens in a pass not run by Movit (see Mixer::subsample_chroma()).
214                 output_ycbcr_format.chroma_subsampling_x = 1;
215                 output_ycbcr_format.chroma_subsampling_y = 1;
216
217                 // Rec. 709 would be the sane thing to do, but it seems many players
218                 // (e.g. MPlayer and VLC) just default to BT.601 coefficients no matter
219                 // what (see discussions in e.g. https://trac.ffmpeg.org/ticket/4978).
220                 // We _do_ set the right flags, though, so that a player that works
221                 // properly doesn't have to guess.
222                 output_ycbcr_format.luma_coefficients = YCBCR_REC_601;
223                 output_ycbcr_format.full_range = false;
224                 output_ycbcr_format.num_levels = 256;
225
226                 chain->add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, output_ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
227                 chain->set_dither_bits(8);
228                 chain->set_output_origin(OUTPUT_ORIGIN_TOP_LEFT);
229         }
230         chain->add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
231
232         chain->finalize();
233         return 0;
234 }
235
236 int LiveInputWrapper_connect_signal(lua_State* L)
237 {
238         assert(lua_gettop(L) == 2);
239         LiveInputWrapper *input = (LiveInputWrapper *)luaL_checkudata(L, 1, "LiveInputWrapper");
240         int signal_num = luaL_checknumber(L, 2);
241         input->connect_signal(signal_num);
242         return 0;
243 }
244
245 int ImageInput_new(lua_State* L)
246 {
247         assert(lua_gettop(L) == 1);
248         std::string filename = checkstdstring(L, 1);
249         return wrap_lua_object<ImageInput>(L, "ImageInput", filename);
250 }
251
252 int WhiteBalanceEffect_new(lua_State* L)
253 {
254         assert(lua_gettop(L) == 0);
255         return wrap_lua_object<WhiteBalanceEffect>(L, "WhiteBalanceEffect");
256 }
257
258 int ResampleEffect_new(lua_State* L)
259 {
260         assert(lua_gettop(L) == 0);
261         return wrap_lua_object<ResampleEffect>(L, "ResampleEffect");
262 }
263
264 int PaddingEffect_new(lua_State* L)
265 {
266         assert(lua_gettop(L) == 0);
267         return wrap_lua_object<PaddingEffect>(L, "PaddingEffect");
268 }
269
270 int IntegralPaddingEffect_new(lua_State* L)
271 {
272         assert(lua_gettop(L) == 0);
273         return wrap_lua_object<IntegralPaddingEffect>(L, "IntegralPaddingEffect");
274 }
275
276 int OverlayEffect_new(lua_State* L)
277 {
278         assert(lua_gettop(L) == 0);
279         return wrap_lua_object<OverlayEffect>(L, "OverlayEffect");
280 }
281
282 int ResizeEffect_new(lua_State* L)
283 {
284         assert(lua_gettop(L) == 0);
285         return wrap_lua_object<ResizeEffect>(L, "ResizeEffect");
286 }
287
288 int MixEffect_new(lua_State* L)
289 {
290         assert(lua_gettop(L) == 0);
291         return wrap_lua_object<MixEffect>(L, "MixEffect");
292 }
293
294 int InputStateInfo_get_width(lua_State* L)
295 {
296         assert(lua_gettop(L) == 2);
297         InputStateInfo *input_state_info = get_input_state_info(L, 1);
298         Theme *theme = get_theme_updata(L);
299         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
300         lua_pushnumber(L, input_state_info->last_width[signal_num]);
301         return 1;
302 }
303
304 int InputStateInfo_get_height(lua_State* L)
305 {
306         assert(lua_gettop(L) == 2);
307         InputStateInfo *input_state_info = get_input_state_info(L, 1);
308         Theme *theme = get_theme_updata(L);
309         int signal_num = theme->map_signal(luaL_checknumber(L, 2));
310         lua_pushnumber(L, input_state_info->last_height[signal_num]);
311         return 1;
312 }
313
314 int Effect_set_float(lua_State *L)
315 {
316         assert(lua_gettop(L) == 3);
317         Effect *effect = (Effect *)get_effect(L, 1);
318         std::string key = checkstdstring(L, 2);
319         float value = luaL_checknumber(L, 3);
320         if (!effect->set_float(key, value)) {
321                 luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
322         }
323         return 0;
324 }
325
326 int Effect_set_int(lua_State *L)
327 {
328         assert(lua_gettop(L) == 3);
329         Effect *effect = (Effect *)get_effect(L, 1);
330         std::string key = checkstdstring(L, 2);
331         float value = luaL_checknumber(L, 3);
332         if (!effect->set_int(key, value)) {
333                 luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), int(value));
334         }
335         return 0;
336 }
337
338 int Effect_set_vec3(lua_State *L)
339 {
340         assert(lua_gettop(L) == 5);
341         Effect *effect = (Effect *)get_effect(L, 1);
342         std::string key = checkstdstring(L, 2);
343         float v[3];
344         v[0] = luaL_checknumber(L, 3);
345         v[1] = luaL_checknumber(L, 4);
346         v[2] = luaL_checknumber(L, 5);
347         if (!effect->set_vec3(key, v)) {
348                 luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(),
349                         v[0], v[1], v[2]);
350         }
351         return 0;
352 }
353
354 int Effect_set_vec4(lua_State *L)
355 {
356         assert(lua_gettop(L) == 6);
357         Effect *effect = (Effect *)get_effect(L, 1);
358         std::string key = checkstdstring(L, 2);
359         float v[4];
360         v[0] = luaL_checknumber(L, 3);
361         v[1] = luaL_checknumber(L, 4);
362         v[2] = luaL_checknumber(L, 5);
363         v[3] = luaL_checknumber(L, 6);
364         if (!effect->set_vec4(key, v)) {
365                 luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(),
366                         v[0], v[1], v[2], v[3]);
367         }
368         return 0;
369 }
370
371 const luaL_Reg EffectChain_funcs[] = {
372         { "new", EffectChain_new },
373         { "add_live_input", EffectChain_add_live_input },
374         { "add_effect", EffectChain_add_effect },
375         { "finalize", EffectChain_finalize },
376         { NULL, NULL }
377 };
378
379 const luaL_Reg LiveInputWrapper_funcs[] = {
380         { "connect_signal", LiveInputWrapper_connect_signal },
381         { NULL, NULL }
382 };
383
384 const luaL_Reg ImageInput_funcs[] = {
385         { "new", ImageInput_new },
386         { "set_float", Effect_set_float },
387         { "set_int", Effect_set_int },
388         { "set_vec3", Effect_set_vec3 },
389         { "set_vec4", Effect_set_vec4 },
390         { NULL, NULL }
391 };
392
393 const luaL_Reg WhiteBalanceEffect_funcs[] = {
394         { "new", WhiteBalanceEffect_new },
395         { "set_float", Effect_set_float },
396         { "set_int", Effect_set_int },
397         { "set_vec3", Effect_set_vec3 },
398         { "set_vec4", Effect_set_vec4 },
399         { NULL, NULL }
400 };
401
402 const luaL_Reg ResampleEffect_funcs[] = {
403         { "new", ResampleEffect_new },
404         { "set_float", Effect_set_float },
405         { "set_int", Effect_set_int },
406         { "set_vec3", Effect_set_vec3 },
407         { "set_vec4", Effect_set_vec4 },
408         { NULL, NULL }
409 };
410
411 const luaL_Reg PaddingEffect_funcs[] = {
412         { "new", PaddingEffect_new },
413         { "set_float", Effect_set_float },
414         { "set_int", Effect_set_int },
415         { "set_vec3", Effect_set_vec3 },
416         { "set_vec4", Effect_set_vec4 },
417         { NULL, NULL }
418 };
419
420 const luaL_Reg IntegralPaddingEffect_funcs[] = {
421         { "new", IntegralPaddingEffect_new },
422         { "set_float", Effect_set_float },
423         { "set_int", Effect_set_int },
424         { "set_vec3", Effect_set_vec3 },
425         { "set_vec4", Effect_set_vec4 },
426         { NULL, NULL }
427 };
428
429 const luaL_Reg OverlayEffect_funcs[] = {
430         { "new", OverlayEffect_new },
431         { "set_float", Effect_set_float },
432         { "set_int", Effect_set_int },
433         { "set_vec3", Effect_set_vec3 },
434         { "set_vec4", Effect_set_vec4 },
435         { NULL, NULL }
436 };
437
438 const luaL_Reg ResizeEffect_funcs[] = {
439         { "new", ResizeEffect_new },
440         { "set_float", Effect_set_float },
441         { "set_int", Effect_set_int },
442         { "set_vec3", Effect_set_vec3 },
443         { "set_vec4", Effect_set_vec4 },
444         { NULL, NULL }
445 };
446
447 const luaL_Reg MixEffect_funcs[] = {
448         { "new", MixEffect_new },
449         { "set_float", Effect_set_float },
450         { "set_int", Effect_set_int },
451         { "set_vec3", Effect_set_vec3 },
452         { "set_vec4", Effect_set_vec4 },
453         { NULL, NULL }
454 };
455
456 const luaL_Reg InputStateInfo_funcs[] = {
457         { "get_width", InputStateInfo_get_width },
458         { "get_height", InputStateInfo_get_height },
459         { NULL, NULL }
460 };
461
462 }  // namespace
463
464 LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bool override_bounce)
465         : theme(theme)
466 {
467         ImageFormat inout_format;
468         inout_format.color_space = COLORSPACE_sRGB;
469
470         // Gamma curve depends on the input signal, and we don't really get any
471         // indications. A camera would be expected to do Rec. 709, but
472         // I haven't checked if any do in practice. However, computers _do_ output
473         // in sRGB gamma (ie., they don't convert from sRGB to Rec. 709), and
474         // I wouldn't really be surprised if most non-professional cameras do, too.
475         // So we pick sRGB as the least evil here.
476         inout_format.gamma_curve = GAMMA_sRGB;
477
478         // The Blackmagic driver docs claim that the device outputs Y'CbCr
479         // according to Rec. 601, but practical testing indicates it definitely
480         // is Rec. 709 (at least up to errors attributable to rounding errors).
481         // Perhaps 601 was only to indicate the subsampling positions, not the
482         // colorspace itself? Tested with a Lenovo X1 gen 3 as input.
483         YCbCrFormat input_ycbcr_format;
484         input_ycbcr_format.chroma_subsampling_x = 2;
485         input_ycbcr_format.chroma_subsampling_y = 1;
486         input_ycbcr_format.cb_x_position = 0.0;
487         input_ycbcr_format.cr_x_position = 0.0;
488         input_ycbcr_format.cb_y_position = 0.5;
489         input_ycbcr_format.cr_y_position = 0.5;
490         input_ycbcr_format.luma_coefficients = YCBCR_REC_709;
491         input_ycbcr_format.full_range = false;
492
493         if (override_bounce) {
494                 input = new NonBouncingYCbCrInput(inout_format, input_ycbcr_format, WIDTH, HEIGHT, YCBCR_INPUT_SPLIT_Y_AND_CBCR);
495         } else {
496                 input = new YCbCrInput(inout_format, input_ycbcr_format, WIDTH, HEIGHT, YCBCR_INPUT_SPLIT_Y_AND_CBCR);
497         }
498         chain->add_input(input);
499 }
500
501 void LiveInputWrapper::connect_signal(int signal_num)
502 {
503         if (global_mixer == nullptr) {
504                 // No data yet.
505                 return;
506         }
507
508         signal_num = theme->map_signal(signal_num);
509
510         BufferedFrame frame = theme->input_state->buffered_frames[signal_num][0];
511         const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
512
513         input->set_texture_num(0, userdata->tex_y[frame.field_number]);
514         input->set_texture_num(1, userdata->tex_cbcr[frame.field_number]);
515         input->set_width(userdata->last_width[frame.field_number]);
516         input->set_height(userdata->last_height[frame.field_number]);
517 }
518
519 Theme::Theme(const char *filename, ResourcePool *resource_pool, unsigned num_cards)
520         : resource_pool(resource_pool), num_cards(num_cards)
521 {
522         L = luaL_newstate();
523         luaL_openlibs(L);
524
525         register_class("EffectChain", EffectChain_funcs); 
526         register_class("LiveInputWrapper", LiveInputWrapper_funcs); 
527         register_class("ImageInput", ImageInput_funcs);
528         register_class("WhiteBalanceEffect", WhiteBalanceEffect_funcs);
529         register_class("ResampleEffect", ResampleEffect_funcs);
530         register_class("PaddingEffect", PaddingEffect_funcs);
531         register_class("IntegralPaddingEffect", IntegralPaddingEffect_funcs);
532         register_class("OverlayEffect", OverlayEffect_funcs);
533         register_class("ResizeEffect", ResizeEffect_funcs);
534         register_class("MixEffect", MixEffect_funcs);
535         register_class("InputStateInfo", InputStateInfo_funcs);
536
537         // Run script.
538         lua_settop(L, 0);
539         if (luaL_dofile(L, filename)) {
540                 fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
541                 lua_pop(L, 1);
542                 exit(1);
543         }
544         assert(lua_gettop(L) == 0);
545
546         // Ask it for the number of channels.
547         lua_getglobal(L, "num_channels");
548
549         if (lua_pcall(L, 0, 1, 0) != 0) {
550                 fprintf(stderr, "error running function `num_channels': %s\n", lua_tostring(L, -1));
551                 exit(1);
552         }
553
554         num_channels = luaL_checknumber(L, 1);
555         lua_pop(L, 1);
556         assert(lua_gettop(L) == 0);
557 }
558
559 void Theme::register_class(const char *class_name, const luaL_Reg *funcs)
560 {
561         assert(lua_gettop(L) == 0);
562         luaL_newmetatable(L, class_name);  // mt = {}
563         lua_pushlightuserdata(L, this);
564         luaL_setfuncs(L, funcs, 1);        // for (name,f in funcs) { mt[name] = f, with upvalue {theme} }
565         lua_pushvalue(L, -1);
566         lua_setfield(L, -2, "__index");    // mt.__index = mt
567         lua_setglobal(L, class_name);      // ClassName = mt
568         assert(lua_gettop(L) == 0);
569 }
570
571 Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned height, InputState input_state) 
572 {
573         Chain chain;
574
575         unique_lock<mutex> lock(m);
576         assert(lua_gettop(L) == 0);
577         lua_getglobal(L, "get_chain");  /* function to be called */
578         lua_pushnumber(L, num);
579         lua_pushnumber(L, t);
580         lua_pushnumber(L, width);
581         lua_pushnumber(L, height);
582         wrap_lua_object<InputStateInfo>(L, "InputStateInfo", input_state);
583
584         if (lua_pcall(L, 5, 2, 0) != 0) {
585                 fprintf(stderr, "error running function `get_chain': %s\n", lua_tostring(L, -1));
586                 exit(1);
587         }
588
589         chain.chain = (EffectChain *)luaL_checkudata(L, -2, "EffectChain");
590         if (!lua_isfunction(L, -1)) {
591                 fprintf(stderr, "Argument #-1 should be a function\n");
592                 exit(1);
593         }
594         lua_pushvalue(L, -1);
595         shared_ptr<LuaRefWithDeleter> funcref(new LuaRefWithDeleter(&m, L, luaL_ref(L, LUA_REGISTRYINDEX)));
596         lua_pop(L, 2);
597         assert(lua_gettop(L) == 0);
598
599         chain.setup_chain = [this, funcref, input_state]{
600                 unique_lock<mutex> lock(m);
601
602                 this->input_state = &input_state;
603
604                 // Set up state, including connecting signals.
605                 lua_rawgeti(L, LUA_REGISTRYINDEX, funcref->get());
606                 if (lua_pcall(L, 0, 0, 0) != 0) {
607                         fprintf(stderr, "error running chain setup callback: %s\n", lua_tostring(L, -1));
608                         exit(1);
609                 }
610                 assert(lua_gettop(L) == 0);
611         };
612
613         // TODO: Can we do better, e.g. by running setup_chain() and seeing what it references?
614         // Actually, setup_chain does maybe hold all the references we need now anyway?
615         for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
616                 for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) {
617                         chain.input_frames.push_back(input_state.buffered_frames[card_index][frame_num].frame);
618                 }
619         }
620
621         return chain;
622 }
623
624 std::string Theme::get_channel_name(unsigned channel)
625 {
626         unique_lock<mutex> lock(m);
627         lua_getglobal(L, "channel_name");
628         lua_pushnumber(L, channel);
629         if (lua_pcall(L, 1, 1, 0) != 0) {
630                 fprintf(stderr, "error running function `channel_nam': %s\n", lua_tostring(L, -1));
631                 exit(1);
632         }
633
634         std::string ret = lua_tostring(L, -1);
635         lua_pop(L, 1);
636         assert(lua_gettop(L) == 0);
637         return ret;
638 }
639
640 bool Theme::get_supports_set_wb(unsigned channel)
641 {
642         unique_lock<mutex> lock(m);
643         lua_getglobal(L, "supports_set_wb");
644         lua_pushnumber(L, channel);
645         if (lua_pcall(L, 1, 1, 0) != 0) {
646                 fprintf(stderr, "error running function `supports_set_wb': %s\n", lua_tostring(L, -1));
647                 exit(1);
648         }
649
650         bool ret = checkbool(L, -1);
651         lua_pop(L, 1);
652         assert(lua_gettop(L) == 0);
653         return ret;
654 }
655
656 void Theme::set_wb(unsigned channel, double r, double g, double b)
657 {
658         unique_lock<mutex> lock(m);
659         lua_getglobal(L, "set_wb");
660         lua_pushnumber(L, channel);
661         lua_pushnumber(L, r);
662         lua_pushnumber(L, g);
663         lua_pushnumber(L, b);
664         if (lua_pcall(L, 4, 0, 0) != 0) {
665                 fprintf(stderr, "error running function `set_wb': %s\n", lua_tostring(L, -1));
666                 exit(1);
667         }
668
669         assert(lua_gettop(L) == 0);
670 }
671
672 std::vector<std::string> Theme::get_transition_names(float t)
673 {
674         unique_lock<mutex> lock(m);
675         lua_getglobal(L, "get_transitions");
676         lua_pushnumber(L, t);
677         if (lua_pcall(L, 1, 1, 0) != 0) {
678                 fprintf(stderr, "error running function `get_transitions': %s\n", lua_tostring(L, -1));
679                 exit(1);
680         }
681
682         std::vector<std::string> ret;
683         lua_pushnil(L);
684         while (lua_next(L, -2) != 0) {
685                 ret.push_back(lua_tostring(L, -1));
686                 lua_pop(L, 1);
687         }
688         lua_pop(L, 1);
689         assert(lua_gettop(L) == 0);
690         return ret;
691 }       
692
693 int Theme::map_signal(int signal_num)
694 {
695         if (signal_num >= int(num_cards)) {
696                 if (signals_warned_about.insert(signal_num).second) {
697                         fprintf(stderr, "WARNING: Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards);
698                         fprintf(stderr, "Mapping to card %d instead.\n", signal_num % num_cards);
699                 }
700                 signal_num %= num_cards;
701         }
702         return signal_num;
703 }
704
705 void Theme::transition_clicked(int transition_num, float t)
706 {
707         unique_lock<mutex> lock(m);
708         lua_getglobal(L, "transition_clicked");
709         lua_pushnumber(L, transition_num);
710         lua_pushnumber(L, t);
711
712         if (lua_pcall(L, 2, 0, 0) != 0) {
713                 fprintf(stderr, "error running function `transition_clicked': %s\n", lua_tostring(L, -1));
714                 exit(1);
715         }
716         assert(lua_gettop(L) == 0);
717 }
718
719 void Theme::channel_clicked(int preview_num)
720 {
721         unique_lock<mutex> lock(m);
722         lua_getglobal(L, "channel_clicked");
723         lua_pushnumber(L, preview_num);
724
725         if (lua_pcall(L, 1, 0, 0) != 0) {
726                 fprintf(stderr, "error running function `channel_clicked': %s\n", lua_tostring(L, -1));
727                 exit(1);
728         }
729         assert(lua_gettop(L) == 0);
730 }