X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=theme.cpp;h=22c940aeb594fced3e409862b3e020df24648a0b;hb=96cb6414f85e0ef4d660b7bd56267303e80fcd05;hp=46803ba92394321412a8617bef8d34acfc082ecf;hpb=bcdbb7bdfcfc45b34ccdf36d3950e7e0af6444bd;p=nageru diff --git a/theme.cpp b/theme.cpp index 46803ba..22c940a 100644 --- a/theme.cpp +++ b/theme.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -26,11 +28,9 @@ #include #include "defs.h" -#include "deinterlace_effect.h" #include "ffmpeg_capture.h" #include "flags.h" #include "image_input.h" -#include "input.h" #include "input_state.h" #include "pbo_frame_allocator.h" @@ -222,7 +222,7 @@ int EffectChain_add_video_input(lua_State* L) // doesn't care about the object anymore. (If we change this, we'd need // to also unregister the signal connection on __gc.) int ret = wrap_lua_object_nonowned( - L, "LiveInputWrapper", theme, chain, bmusb::PixelFormat_8BitRGBA, + L, "LiveInputWrapper", theme, chain, (*capture)->get_current_pixel_format(), /*override_bounce=*/false, deinterlace); if (ret == 1) { Theme *theme = get_theme_updata(L); @@ -341,17 +341,51 @@ int ImageInput_new(lua_State* L) int VideoInput_new(lua_State* L) { - assert(lua_gettop(L) == 1); + assert(lua_gettop(L) == 2); string filename = checkstdstring(L, 1); + int pixel_format = luaL_checknumber(L, 2); + if (pixel_format != bmusb::PixelFormat_8BitYCbCrPlanar && + pixel_format != bmusb::PixelFormat_8BitBGRA) { + fprintf(stderr, "WARNING: Invalid enum %d used for video format, choosing Y'CbCr.\n", + pixel_format); + pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar; + } int ret = wrap_lua_object_nonowned(L, "VideoInput", filename, global_flags.width, global_flags.height); if (ret == 1) { - Theme *theme = get_theme_updata(L); FFmpegCapture **capture = (FFmpegCapture **)lua_touserdata(L, -1); + (*capture)->set_pixel_format(bmusb::PixelFormat(pixel_format)); + + Theme *theme = get_theme_updata(L); theme->register_video_input(*capture); } return ret; } +int VideoInput_rewind(lua_State* L) +{ + assert(lua_gettop(L) == 1); + FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput"); + (*video_input)->rewind(); + return 0; +} + +int VideoInput_change_rate(lua_State* L) +{ + assert(lua_gettop(L) == 2); + FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput"); + double new_rate = luaL_checknumber(L, 2); + (*video_input)->change_rate(new_rate); + return 0; +} + +int VideoInput_get_signal_num(lua_State* L) +{ + assert(lua_gettop(L) == 1); + FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput"); + lua_pushnumber(L, -1 - (*video_input)->get_card_index()); + return 1; +} + int WhiteBalanceEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); @@ -553,6 +587,9 @@ const luaL_Reg ImageInput_funcs[] = { const luaL_Reg VideoInput_funcs[] = { { "new", VideoInput_new }, + { "rewind", VideoInput_rewind }, + { "change_rate", VideoInput_change_rate }, + { "get_signal_num", VideoInput_get_signal_num }, { NULL, NULL } }; @@ -673,8 +710,10 @@ LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bmusb::Pixe num_inputs = 1; } - if (pixel_format == bmusb::PixelFormat_8BitRGBA) { + if (pixel_format == bmusb::PixelFormat_8BitBGRA) { for (unsigned i = 0; i < num_inputs; ++i) { + // We upload our textures ourselves, and Movit swaps + // R and B in the shader if we specify BGRA, so lie and say RGBA. if (global_flags.can_disable_srgb_decoder) { rgba_inputs.push_back(new sRGBSwitchingFlatInput(inout_format, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, global_flags.width, global_flags.height)); } else { @@ -688,13 +727,11 @@ LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bmusb::Pixe chain->add_effect(deinterlace_effect, reverse_inputs); } } else { - assert(pixel_format == bmusb::PixelFormat_8BitYCbCr || pixel_format == bmusb::PixelFormat_10BitYCbCr); - // The Blackmagic driver docs claim that the device outputs Y'CbCr - // according to Rec. 601, but practical testing indicates it definitely - // is Rec. 709 (at least up to errors attributable to rounding errors). - // Perhaps 601 was only to indicate the subsampling positions, not the - // colorspace itself? Tested with a Lenovo X1 gen 3 as input. - YCbCrFormat input_ycbcr_format; + assert(pixel_format == bmusb::PixelFormat_8BitYCbCr || + pixel_format == bmusb::PixelFormat_10BitYCbCr || + pixel_format == bmusb::PixelFormat_8BitYCbCrPlanar); + + // Most of these settings will be overridden later if using PixelFormat_8BitYCbCrPlanar. input_ycbcr_format.chroma_subsampling_x = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? 1 : 2; input_ycbcr_format.chroma_subsampling_y = 1; input_ycbcr_format.num_levels = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? 1024 : 256; @@ -702,12 +739,19 @@ LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bmusb::Pixe input_ycbcr_format.cr_x_position = 0.0; input_ycbcr_format.cb_y_position = 0.5; input_ycbcr_format.cr_y_position = 0.5; - input_ycbcr_format.luma_coefficients = YCBCR_REC_709; - input_ycbcr_format.full_range = false; + input_ycbcr_format.luma_coefficients = YCBCR_REC_709; // Will be overridden later even if not planar. + input_ycbcr_format.full_range = false; // Will be overridden later even if not planar. for (unsigned i = 0; i < num_inputs; ++i) { // When using 10-bit input, we're converting to interleaved through v210Converter. - YCbCrInputSplitting splitting = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? YCBCR_INPUT_INTERLEAVED : YCBCR_INPUT_SPLIT_Y_AND_CBCR; + YCbCrInputSplitting splitting; + if (pixel_format == bmusb::PixelFormat_10BitYCbCr) { + splitting = YCBCR_INPUT_INTERLEAVED; + } else if (pixel_format == bmusb::PixelFormat_8BitYCbCr) { + splitting = YCBCR_INPUT_SPLIT_Y_AND_CBCR; + } else { + splitting = YCBCR_INPUT_PLANAR; + } if (override_bounce) { ycbcr_inputs.push_back(new NonBouncingYCbCrInput(inout_format, input_ycbcr_format, global_flags.width, global_flags.height, splitting)); } else { @@ -748,6 +792,31 @@ void LiveInputWrapper::connect_signal_raw(int signal_num) height = userdata->last_height[first_frame.field_number]; } + movit::YCbCrLumaCoefficients ycbcr_coefficients = theme->input_state->ycbcr_coefficients[signal_num]; + bool full_range = theme->input_state->full_range[signal_num]; + + if (theme->input_state->ycbcr_coefficients_auto[signal_num]) { + full_range = false; + + // The Blackmagic driver docs claim that the device outputs Y'CbCr + // according to Rec. 601, but this seems to indicate the subsampling + // positions only, as they publish Y'CbCr → RGB formulas that are + // different for HD and SD (corresponding to Rec. 709 and 601, respectively), + // and a Lenovo X1 gen 3 I used to test definitely outputs Rec. 709 + // (at least up to rounding error). Other devices seem to use Rec. 601 + // even on HD resolutions. Nevertheless, Rec. 709 _is_ the right choice + // for HD, so we default to that if the user hasn't set anything. + if (height >= 720) { + ycbcr_coefficients = YCBCR_REC_709; + } else { + ycbcr_coefficients = YCBCR_REC_601; + } + } + + // This is a global, but it doesn't really matter. + input_ycbcr_format.luma_coefficients = ycbcr_coefficients; + input_ycbcr_format.full_range = full_range; + BufferedFrame last_good_frame = first_frame; for (unsigned i = 0; i < max(ycbcr_inputs.size(), rgba_inputs.size()); ++i) { BufferedFrame frame = theme->input_state->buffered_frames[signal_num][i]; @@ -771,18 +840,28 @@ void LiveInputWrapper::connect_signal_raw(int signal_num) case bmusb::PixelFormat_8BitYCbCr: ycbcr_inputs[i]->set_texture_num(0, userdata->tex_y[frame.field_number]); ycbcr_inputs[i]->set_texture_num(1, userdata->tex_cbcr[frame.field_number]); - ycbcr_inputs[i]->set_width(this_width); - ycbcr_inputs[i]->set_height(this_height); + ycbcr_inputs[i]->change_ycbcr_format(input_ycbcr_format); + ycbcr_inputs[i]->set_width(width); + ycbcr_inputs[i]->set_height(height); + break; + case bmusb::PixelFormat_8BitYCbCrPlanar: + 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); + ycbcr_inputs[i]->set_width(width); + ycbcr_inputs[i]->set_height(height); break; case bmusb::PixelFormat_10BitYCbCr: ycbcr_inputs[i]->set_texture_num(0, userdata->tex_444[frame.field_number]); - ycbcr_inputs[i]->set_width(this_width); - ycbcr_inputs[i]->set_height(this_height); + ycbcr_inputs[i]->change_ycbcr_format(input_ycbcr_format); + ycbcr_inputs[i]->set_width(width); + ycbcr_inputs[i]->set_height(height); break; - case bmusb::PixelFormat_8BitRGBA: + case bmusb::PixelFormat_8BitBGRA: rgba_inputs[i]->set_texture_num(userdata->tex_rgba[frame.field_number]); - rgba_inputs[i]->set_width(this_width); - rgba_inputs[i]->set_height(this_height); + rgba_inputs[i]->set_width(width); + rgba_inputs[i]->set_height(height); break; default: assert(false); @@ -822,6 +901,7 @@ Theme::Theme(const string &filename, const vector &search_dirs, Resource L = luaL_newstate(); luaL_openlibs(L); + register_constants(); register_class("EffectChain", EffectChain_funcs); register_class("LiveInputWrapper", LiveInputWrapper_funcs); register_class("ImageInput", ImageInput_funcs); @@ -879,6 +959,26 @@ Theme::~Theme() lua_close(L); } +void Theme::register_constants() +{ + // Set Nageru.VIDEO_FORMAT_BGRA = bmusb::PixelFormat_8BitBGRA, etc. + const vector> constants = { + { "VIDEO_FORMAT_BGRA", bmusb::PixelFormat_8BitBGRA }, + { "VIDEO_FORMAT_YCBCR", bmusb::PixelFormat_8BitYCbCrPlanar }, + }; + + lua_newtable(L); // t = {} + + for (const pair &constant : constants) { + lua_pushstring(L, constant.first.c_str()); + lua_pushinteger(L, constant.second); + lua_settable(L, 1); // t[key] = value + } + + lua_setglobal(L, "Nageru"); // Nageru = t + assert(lua_gettop(L) == 0); +} + void Theme::register_class(const char *class_name, const luaL_Reg *funcs) { assert(lua_gettop(L) == 0); @@ -1063,6 +1163,11 @@ vector Theme::get_transition_names(float t) int Theme::map_signal(int signal_num) { + // Negative numbers map to raw signals. + if (signal_num < 0) { + return -1 - signal_num; + } + unique_lock lock(map_m); if (signal_to_card_mapping.count(signal_num)) { return signal_to_card_mapping[signal_num];