#include <stdarg.h>
#include <lauxlib.h>
#include <lua.hpp>
+#include <movit/blur_effect.h>
#include <movit/deinterlace_effect.h>
#include <movit/effect.h>
#include <movit/effect_chain.h>
#include <movit/resample_effect.h>
#include <movit/resize_effect.h>
#include <movit/util.h>
+#include <movit/unsharp_mask_effect.h>
#include <movit/white_balance_effect.h>
#include <movit/ycbcr.h>
#include <movit/ycbcr_input.h>
return new MixEffect;
case LIFT_GAMMA_GAIN_EFFECT:
return new LiftGammaGainEffect;
+ case BLUR_EFFECT:
+ return new BlurEffect;
+ case UNSHARP_MASK_EFFECT:
+ return new UnsharpMaskEffect;
default:
fprintf(stderr, "Unhandled effect type %d\n", effect_type);
abort();
}
output_ycbcr_format.full_range = false;
- output_ycbcr_format.num_levels = 1 << global_flags.x264_bit_depth;
+ output_ycbcr_format.num_levels = 1 << global_flags.bit_depth;
- GLenum type = global_flags.x264_bit_depth > 8 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
+ GLenum type = global_flags.bit_depth > 8 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
chain->add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, output_ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR, type);
if (global_flags.use_zerocopy) {
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.
}
- chain->set_dither_bits(global_flags.x264_bit_depth > 8 ? 16 : 8);
+ chain->set_dither_bits(global_flags.bit_depth > 8 ? 16 : 8);
chain->set_output_origin(OUTPUT_ORIGIN_TOP_LEFT);
} else {
chain->add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
bool override_bounce = checkbool(L, 2);
bool deinterlace = checkbool(L, 3);
- bmusb::PixelFormat pixel_format = global_flags.ten_bit_input ? bmusb::PixelFormat_10BitYCbCr : bmusb::PixelFormat_8BitYCbCr;
+ bmusb::PixelFormat pixel_format = global_flags.bit_depth > 8 ? bmusb::PixelFormat_10BitYCbCr : bmusb::PixelFormat_8BitYCbCr;
// Needs to be nonowned to match add_video_input (see below).
return wrap_lua_object_nonowned<LiveInputWrapper>(L, "LiveInputWrapper", theme, chain, pixel_format, override_bounce, deinterlace, /*user_connectable=*/true);
return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", LIFT_GAMMA_GAIN_EFFECT);
}
+int BlurEffect_new(lua_State* L)
+{
+ assert(lua_gettop(L) == 0);
+ return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", BLUR_EFFECT);
+}
+
+int UnsharpMaskEffect_new(lua_State* L)
+{
+ assert(lua_gettop(L) == 0);
+ return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", UNSHARP_MASK_EFFECT);
+}
+
int InputStateInfo_get_width(lua_State* L)
{
assert(lua_gettop(L) == 2);
{ NULL, NULL }
};
+const luaL_Reg BlurEffect_funcs[] = {
+ { "new", BlurEffect_new },
+ { NULL, NULL }
+};
+
+const luaL_Reg UnsharpMaskEffect_funcs[] = {
+ { "new", UnsharpMaskEffect_new },
+ { NULL, NULL }
+};
+
// End of effects.
const luaL_Reg InputStateInfo_funcs[] = {
ycbcr_inputs[i]->set_texture_num(0, userdata->tex_y[frame.field_number]);
ycbcr_inputs[i]->set_texture_num(1, userdata->tex_cb[frame.field_number]);
ycbcr_inputs[i]->set_texture_num(2, userdata->tex_cr[frame.field_number]);
- ycbcr_inputs[i]->change_ycbcr_format(userdata->ycbcr_format);
+ // YCbCrPlanar is used for video streams, where we can have metadata from the mux.
+ // Prefer that if there's no override. (Overrides are only available when using
+ // video as SRT cards.)
+ if (input_state.ycbcr_coefficients_auto[card_idx]) {
+ ycbcr_inputs[i]->change_ycbcr_format(userdata->ycbcr_format);
+ } else {
+ ycbcr_inputs[i]->change_ycbcr_format(input_ycbcr_format);
+ }
ycbcr_inputs[i]->set_width(width);
ycbcr_inputs[i]->set_height(height);
break;
return 0;
}
-Theme::Theme(const string &filename, const vector<string> &search_dirs, ResourcePool *resource_pool, unsigned num_cards)
- : resource_pool(resource_pool), num_cards(num_cards), signal_to_card_mapping(global_flags.default_stream_mapping)
+Theme::Theme(const string &filename, const vector<string> &search_dirs, ResourcePool *resource_pool)
+ : resource_pool(resource_pool), signal_to_card_mapping(global_flags.default_stream_mapping)
{
// Defaults.
channel_names[0] = "Live";
register_class("ResizeEffect", ResizeEffect_funcs, RESIZE_EFFECT);
register_class("MultiplyEffect", MultiplyEffect_funcs, MULTIPLY_EFFECT);
register_class("MixEffect", MixEffect_funcs, MIX_EFFECT);
+ register_class("BlurEffect", BlurEffect_funcs, BLUR_EFFECT);
+ register_class("UnsharpMaskEffect", UnsharpMaskEffect_funcs, UNSHARP_MASK_EFFECT);
register_class("LiftGammaGainEffect", LiftGammaGainEffect_funcs, LIFT_GAMMA_GAIN_EFFECT);
register_class("InputStateInfo", InputStateInfo_funcs);
register_class("ThemeMenu", ThemeMenu_funcs);
// TODO: Can we do better, e.g. by running setup_chain() and seeing what it references?
// Actually, setup_chain does maybe hold all the references we need now anyway?
- chain.input_frames.reserve(num_cards * FRAME_HISTORY_LENGTH);
- for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
+ chain.input_frames.reserve(MAX_VIDEO_CARDS * FRAME_HISTORY_LENGTH);
+ for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) {
chain.input_frames.push_back(input_state.buffered_frames[card_index][frame_num].frame);
}
}
int card_index;
- if (global_flags.output_card != -1 && num_cards > 1) {
+ if (global_flags.output_card != -1) {
// Try to exclude the output card from the default card_index.
- card_index = signal_num % (num_cards - 1);
+ card_index = signal_num % (global_flags.max_num_cards - 1);
if (card_index >= global_flags.output_card) {
++card_index;
}
- if (signal_num >= int(num_cards - 1)) {
+ if (signal_num >= int(global_flags.max_num_cards - 1)) {
print_warning(L, "Theme asked for input %d, but we only have %u input card(s) (card %d is busy with output).\n",
- signal_num, num_cards - 1, global_flags.output_card);
+ signal_num, global_flags.max_num_cards - 1, global_flags.output_card);
fprintf(stderr, "Mapping to card %d instead.\n", card_index);
}
} else {
- card_index = signal_num % num_cards;
- if (signal_num >= int(num_cards)) {
- print_warning(L, "Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards);
+ card_index = signal_num % global_flags.max_num_cards;
+ if (signal_num >= int(global_flags.max_num_cards)) {
+ print_warning(L, "Theme asked for input %d, but we only have %u card(s).\n", signal_num, global_flags.max_num_cards);
fprintf(stderr, "Mapping to card %d instead.\n", card_index);
}
}
+ global_mixer->force_card_active(card_index);
signal_to_card_mapping[signal_num] = card_index;
return card_index;
}
void Theme::set_signal_mapping(int signal_num, int card_idx)
{
lock_guard<mutex> lock(map_m);
- assert(card_idx < int(num_cards));
+ assert(card_idx < MAX_VIDEO_CARDS);
signal_to_card_mapping[signal_num] = card_idx;
}
lua_pop(L, 1);
unsigned flags = 0;
- if (lua_objlen(L, -1) > 2) {
- lua_rawgeti(L, -1, 3);
+ if (lua_objlen(L, index) > 2) {
+ lua_rawgeti(L, index, 3);
flags = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
assert(lua_gettop(L) == 0);
return text;
}
+
+void Theme::remove_card(unsigned card_index)
+{
+ lock_guard<mutex> lock(map_m);
+ for (auto it = signal_to_card_mapping.begin(); it != signal_to_card_mapping.end(); ) {
+ if (it->second == int(card_index)) {
+ it = signal_to_card_mapping.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}