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.
void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned field, unsigned width, unsigned height, unsigned v210_width)
{
bool first;
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;
first = userdata->tex_v210[field] == 0 || userdata->tex_444[field] == 0;
} else {
first = userdata->tex_y[field] == 0 || userdata->tex_cbcr[field] == 0;
// 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.
// 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);
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);
if (card->output.get() != output) {
card->output.reset(output);
}
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->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) {
}
card->capture->set_video_frame_allocator(card->frame_allocator.get());
if (card->surface == nullptr) {
}
while (!card->new_frames.empty()) card->new_frames.pop_front();
card->last_timecode = -1;
}
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().
card->capture->configure_card();
// NOTE: start_bm_capture() happens in thread_func().
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, userdata->pbo);
check_error();
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);
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);
#include <stdio.h>
#include <cstddef>
#include <stdio.h>
#include <cstddef>
#include "v210_converter.h"
using namespace std;
#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) {
{
userdata.reset(new Userdata[num_queued_frames]);
for (size_t i = 0; i < num_queued_frames; ++i) {
frame.size = frame_size;
frame.userdata = &userdata[i];
userdata[i].pbo = pbo;
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.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.
// 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);
glGenTextures(2, userdata[i].tex_v210);
check_error();
glGenTextures(2, userdata[i].tex_444);
userdata[i].last_has_signal = false;
userdata[i].last_is_connected = false;
for (unsigned field = 0; field < 2; ++field) {
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
const size_t v210_width = v210Converter::get_minimum_v210_texture_width(width);
// Seemingly we need to set the minification filter even though
check_error();
glDeleteBuffers(1, &pbo);
check_error();
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);
glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_v210);
check_error();
glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_444);
public:
// Note: You need to have an OpenGL context when calling
// the constructor.
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,
GLuint width, GLuint height,
size_t num_queued_frames = 16, // FIXME: should be 6
GLenum buffer = GL_PIXEL_UNPACK_BUFFER_ARB,
struct Userdata {
GLuint pbo;
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.
// 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_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:
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;
std::mutex freelist_mutex;
std::queue<Frame> freelist;
GLenum buffer;
EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain");
bool override_bounce = checkbool(L, 2);
bool deinterlace = checkbool(L, 3);
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)
}
int EffectChain_add_effect(lua_State* L)
-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)
+ pixel_format(pixel_format),
deinterlace(deinterlace)
{
ImageFormat inout_format;
deinterlace(deinterlace)
{
ImageFormat inout_format;
// 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;
// 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.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;
input_ycbcr_format.cb_x_position = 0.0;
input_ycbcr_format.cr_x_position = 0.0;
input_ycbcr_format.cb_y_position = 0.5;
}
for (unsigned i = 0; i < num_inputs; ++i) {
// When using 10-bit input, we're converting to interleaved through v210Converter.
}
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 {
if (override_bounce) {
inputs.push_back(new NonBouncingYCbCrInput(inout_format, input_ycbcr_format, global_flags.width, global_flags.height, splitting));
} else {
userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata;
}
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]);
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]);
#include <string>
#include <vector>
#include <string>
#include <vector>
+#include "bmusb/bmusb.h"
#include "ref_counted_frame.h"
struct InputState;
#include "ref_counted_frame.h"
struct InputState;
// the mixer, and communicates that state over to the actual YCbCrInput.
class LiveInputWrapper {
public:
// 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
void connect_signal(int signal_num);
movit::Effect *get_effect() const
private:
Theme *theme; // Not owned by us.
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;
std::vector<movit::YCbCrInput *> inputs; // Multiple ones if deinterlacing. Owned by the chain.
movit::Effect *deinterlace_effect = nullptr; // Owned by the chain.
bool deinterlace;