From: Steinar H. Gunderson Date: Sat, 8 Apr 2017 20:52:18 +0000 (+0200) Subject: Give frames a pixel format. X-Git-Tag: 1.6.0~61 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=ba2dc655c098912b8860676596fe89c1305affe9 Give frames a pixel format. This is preliminary work towards supporting frames with alpha. It generally reduces the dependency on the global --10-bit-input flag a bit, which is also a good thing. --- diff --git a/mixer.cpp b/mixer.cpp index d805bcc..b00c4af 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -81,7 +81,7 @@ void insert_new_frame(RefCountedFrame frame, unsigned field_num, bool interlaced void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned field, unsigned width, unsigned height, unsigned v210_width) { bool first; - if (global_flags.ten_bit_input) { + if (userdata->pixel_format == bmusb::PixelFormat_10BitYCbCr) { first = userdata->tex_v210[field] == 0 || userdata->tex_444[field] == 0; } else { first = userdata->tex_y[field] == 0 || userdata->tex_cbcr[field] == 0; @@ -93,7 +93,7 @@ void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned f // We changed resolution since last use of this texture, so we need to create // a new object. Note that this each card has its own PBOFrameAllocator, // we don't need to worry about these flip-flopping between resolutions. - if (global_flags.ten_bit_input) { + if (userdata->pixel_format == bmusb::PixelFormat_10BitYCbCr) { glBindTexture(GL_TEXTURE_2D, userdata->tex_444[field]); check_error(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr); @@ -361,9 +361,12 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool if (card->output.get() != output) { card->output.reset(output); } + + bmusb::PixelFormat pixel_format = global_flags.ten_bit_input ? PixelFormat_10BitYCbCr : PixelFormat_8BitYCbCr; + card->capture->set_frame_callback(bind(&Mixer::bm_frame, this, card_index, _1, _2, _3, _4, _5, _6, _7)); if (card->frame_allocator == nullptr) { - card->frame_allocator.reset(new PBOFrameAllocator(8 << 20, global_flags.width, global_flags.height)); // 8 MB. + card->frame_allocator.reset(new PBOFrameAllocator(pixel_format, 8 << 20, global_flags.width, global_flags.height)); // 8 MB. } card->capture->set_video_frame_allocator(card->frame_allocator.get()); if (card->surface == nullptr) { @@ -371,7 +374,7 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool } while (!card->new_frames.empty()) card->new_frames.pop_front(); card->last_timecode = -1; - card->capture->set_pixel_format(global_flags.ten_bit_input ? PixelFormat_10BitYCbCr : PixelFormat_8BitYCbCr); + card->capture->set_pixel_format(pixel_format); card->capture->configure_card(); // NOTE: start_bm_capture() happens in thread_func(). @@ -595,7 +598,7 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, glBindBuffer(GL_PIXEL_UNPACK_BUFFER, userdata->pbo); check_error(); - if (global_flags.ten_bit_input) { + if (userdata->pixel_format == bmusb::PixelFormat_10BitYCbCr) { size_t field_start = video_offset + video_format.stride * field_start_line; upload_texture(userdata->tex_v210[field], v210_width, video_format.height, video_format.stride, interlaced_stride, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, field_start); v210_converter->convert(userdata->tex_v210[field], userdata->tex_444[field], video_format.width, video_format.height); diff --git a/pbo_frame_allocator.cpp b/pbo_frame_allocator.cpp index bfc65f2..87f11eb 100644 --- a/pbo_frame_allocator.cpp +++ b/pbo_frame_allocator.cpp @@ -7,13 +7,12 @@ #include #include -#include "flags.h" #include "v210_converter.h" using namespace std; -PBOFrameAllocator::PBOFrameAllocator(size_t frame_size, GLuint width, GLuint height, size_t num_queued_frames, GLenum buffer, GLenum permissions, GLenum map_bits) - : buffer(buffer) +PBOFrameAllocator::PBOFrameAllocator(bmusb::PixelFormat pixel_format, size_t frame_size, GLuint width, GLuint height, size_t num_queued_frames, GLenum buffer, GLenum permissions, GLenum map_bits) + : pixel_format(pixel_format), buffer(buffer) { userdata.reset(new Userdata[num_queued_frames]); for (size_t i = 0; i < num_queued_frames; ++i) { @@ -32,18 +31,19 @@ PBOFrameAllocator::PBOFrameAllocator(size_t frame_size, GLuint width, GLuint hei frame.size = frame_size; frame.userdata = &userdata[i]; userdata[i].pbo = pbo; + userdata[i].pixel_format = pixel_format; frame.owner = this; // For 8-bit Y'CbCr, we ask the driver to split Y' and Cb/Cr // into separate textures. For 10-bit, the input format (v210) // is complicated enough that we need to interpolate up to 4:4:4, // which we do in a compute shader ourselves. - frame.interleaved = !global_flags.ten_bit_input; + frame.interleaved = (pixel_format == bmusb::PixelFormat_8BitYCbCr); // Create textures. We don't allocate any data for the second field at this point // (just create the texture state with the samplers), since our default assumed // resolution is progressive. - if (global_flags.ten_bit_input) { + if (pixel_format == bmusb::PixelFormat_10BitYCbCr) { glGenTextures(2, userdata[i].tex_v210); check_error(); glGenTextures(2, userdata[i].tex_444); @@ -67,7 +67,7 @@ PBOFrameAllocator::PBOFrameAllocator(size_t frame_size, GLuint width, GLuint hei userdata[i].last_has_signal = false; userdata[i].last_is_connected = false; for (unsigned field = 0; field < 2; ++field) { - if (global_flags.ten_bit_input) { + if (pixel_format == bmusb::PixelFormat_10BitYCbCr) { const size_t v210_width = v210Converter::get_minimum_v210_texture_width(width); // Seemingly we need to set the minification filter even though @@ -146,7 +146,7 @@ PBOFrameAllocator::~PBOFrameAllocator() check_error(); glDeleteBuffers(1, &pbo); check_error(); - if (global_flags.ten_bit_input) { + if (pixel_format == bmusb::PixelFormat_10BitYCbCr) { glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_v210); check_error(); glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_444); diff --git a/pbo_frame_allocator.h b/pbo_frame_allocator.h index da3bd2e..4ee3baa 100644 --- a/pbo_frame_allocator.h +++ b/pbo_frame_allocator.h @@ -17,7 +17,8 @@ class PBOFrameAllocator : public bmusb::FrameAllocator { public: // Note: You need to have an OpenGL context when calling // the constructor. - PBOFrameAllocator(size_t frame_size, + PBOFrameAllocator(bmusb::PixelFormat pixel_format, + size_t frame_size, GLuint width, GLuint height, size_t num_queued_frames = 16, // FIXME: should be 6 GLenum buffer = GL_PIXEL_UNPACK_BUFFER_ARB, @@ -30,16 +31,23 @@ public: struct Userdata { GLuint pbo; + // NOTE: These frames typically go into LiveInputWrapper, which is + // configured to accept one type of frame only. In other words, + // the existence of a format field doesn't mean you can set it + // freely at runtime. + bmusb::PixelFormat pixel_format; + // The second set is only used for the second field of interlaced inputs. - GLuint tex_y[2], tex_cbcr[2]; // For 8-bit. - GLuint tex_v210[2], tex_444[2]; // For 10-bit. + GLuint tex_y[2], tex_cbcr[2]; // For FRAME_FORMAT_YCBCR_8BIT. + GLuint tex_v210[2], tex_444[2]; // For FRAME_FORMAT_YCBCR_10BIT. GLuint last_width[2], last_height[2]; - GLuint last_v210_width[2]; // For 10-bit. + GLuint last_v210_width[2]; // FRAME_FORMAT_YCBCR_10BIT. bool last_interlaced, last_has_signal, last_is_connected; unsigned last_frame_rate_nom, last_frame_rate_den; }; private: + bmusb::PixelFormat pixel_format; std::mutex freelist_mutex; std::queue freelist; GLenum buffer; diff --git a/theme.cpp b/theme.cpp index 0cc498e..9e04740 100644 --- a/theme.cpp +++ b/theme.cpp @@ -202,7 +202,8 @@ int EffectChain_add_live_input(lua_State* L) EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain"); bool override_bounce = checkbool(L, 2); bool deinterlace = checkbool(L, 3); - return wrap_lua_object(L, "LiveInputWrapper", theme, chain, override_bounce, deinterlace); + bmusb::PixelFormat pixel_format = global_flags.ten_bit_input ? bmusb::PixelFormat_10BitYCbCr : bmusb::PixelFormat_8BitYCbCr; + return wrap_lua_object(L, "LiveInputWrapper", theme, chain, pixel_format, override_bounce, deinterlace); } int EffectChain_add_effect(lua_State* L) @@ -595,8 +596,9 @@ const luaL_Reg InputStateInfo_funcs[] = { } // namespace -LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bool override_bounce, bool deinterlace) +LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bmusb::PixelFormat pixel_format, bool override_bounce, bool deinterlace) : theme(theme), + pixel_format(pixel_format), deinterlace(deinterlace) { ImageFormat inout_format; @@ -616,9 +618,9 @@ LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bool overri // 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; - input_ycbcr_format.chroma_subsampling_x = global_flags.ten_bit_input ? 1 : 2; + 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 = global_flags.ten_bit_input ? 1024 : 256; + input_ycbcr_format.num_levels = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? 1024 : 256; input_ycbcr_format.cb_x_position = 0.0; input_ycbcr_format.cr_x_position = 0.0; input_ycbcr_format.cb_y_position = 0.5; @@ -643,7 +645,7 @@ LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bool overri } for (unsigned i = 0; i < num_inputs; ++i) { // When using 10-bit input, we're converting to interleaved through v210Converter. - YCbCrInputSplitting splitting = global_flags.ten_bit_input ? YCBCR_INPUT_INTERLEAVED : YCBCR_INPUT_SPLIT_Y_AND_CBCR; + YCbCrInputSplitting splitting = (pixel_format == bmusb::PixelFormat_10BitYCbCr) ? YCBCR_INPUT_INTERLEAVED : YCBCR_INPUT_SPLIT_Y_AND_CBCR; if (override_bounce) { inputs.push_back(new NonBouncingYCbCrInput(inout_format, input_ycbcr_format, global_flags.width, global_flags.height, splitting)); } else { @@ -696,7 +698,8 @@ void LiveInputWrapper::connect_signal(int signal_num) userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata; } - if (global_flags.ten_bit_input) { + assert(userdata->pixel_format == pixel_format); + if (pixel_format == bmusb::PixelFormat_10BitYCbCr) { inputs[i]->set_texture_num(0, userdata->tex_444[frame.field_number]); } else { inputs[i]->set_texture_num(0, userdata->tex_y[frame.field_number]); diff --git a/theme.h b/theme.h index d5657fe..4394513 100644 --- a/theme.h +++ b/theme.h @@ -10,6 +10,7 @@ #include #include +#include "bmusb/bmusb.h" #include "ref_counted_frame.h" struct InputState; @@ -87,7 +88,7 @@ private: // the mixer, and communicates that state over to the actual YCbCrInput. class LiveInputWrapper { public: - LiveInputWrapper(Theme *theme, movit::EffectChain *chain, bool override_bounce, bool deinterlace); + LiveInputWrapper(Theme *theme, movit::EffectChain *chain, bmusb::PixelFormat pixel_format, bool override_bounce, bool deinterlace); void connect_signal(int signal_num); movit::Effect *get_effect() const @@ -101,6 +102,7 @@ public: private: Theme *theme; // Not owned by us. + bmusb::PixelFormat pixel_format; std::vector inputs; // Multiple ones if deinterlacing. Owned by the chain. movit::Effect *deinterlace_effect = nullptr; // Owned by the chain. bool deinterlace;