]> git.sesse.net Git - nageru/commitdiff
Give frames a pixel format.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 8 Apr 2017 20:52:18 +0000 (22:52 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 12 Apr 2017 16:58:36 +0000 (18:58 +0200)
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.

mixer.cpp
pbo_frame_allocator.cpp
pbo_frame_allocator.h
theme.cpp
theme.h

index d805bcc124bfe8bb1e858b17aa1ee9ac0d440114..b00c4af0ef1a52d03ee2e6cdb453384209acb85a 100644 (file)
--- 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);
index bfc65f2efe918bf0c0e988d75459d55a74023773..87f11ebde76826257de97fe01cd4e447091a6b23 100644 (file)
@@ -7,13 +7,12 @@
 #include <stdio.h>
 #include <cstddef>
 
-#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);
index da3bd2e6d05d7f8668b90f9c61d4b3dd30400d22..4ee3baaf5dbba6b0ea7f21b8c405b206af2d0142 100644 (file)
@@ -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<Frame> freelist;
        GLenum buffer;
index 0cc498e8ba0a4297be74bb320440f6c75cba2f7f..9e04740d60b3636a636bf1460a4bfd4afb9e3a0a 100644 (file)
--- 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<LiveInputWrapper>(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<LiveInputWrapper>(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 d5657fe0309b2711b8a70ca76ac3227a60d927a1..4394513cceeb53a8729994e6f1966e152f53ac5b 100644 (file)
--- a/theme.h
+++ b/theme.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#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<movit::YCbCrInput *> inputs;  // Multiple ones if deinterlacing. Owned by the chain.
        movit::Effect *deinterlace_effect = nullptr;  // Owned by the chain.
        bool deinterlace;