#include "context.h"
#include "bmusb.h"
#include "pbo_frame_allocator.h"
+#include "mixer.h"
+#include "ref_counted_gl_sync.h"
using namespace movit;
using namespace std;
using namespace std::placeholders;
-// shared between all EGL contexts
-EGLDisplay egl_display;
-EGLSurface egl_surface;
-EGLConfig ecfg;
-EGLint ctxattr[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
- EGL_CONTEXT_MINOR_VERSION_KHR, 1,
- //EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
- EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
- EGL_NONE
-};
+Source current_source = SOURCE_INPUT1;
-EGLConfig pbuffer_ecfg;
+ResourcePool *resource_pool;
-std::mutex bmusb_mutex; // protects <cards>
+std::mutex display_frame_mutex;
+DisplayFrame current_display_frame, ready_display_frame; // protected by <frame_mutex>
+bool has_current_display_frame = false, has_ready_display_frame = false; // protected by <frame_mutex>
+std::mutex bmusb_mutex;
struct CaptureCard {
BMUSBCapture *usb;
GLsync new_data_ready_fence; // Whether new_frame is ready for rendering.
std::condition_variable new_data_ready_changed; // Set whenever new_data_ready is changed.
};
-CaptureCard cards[NUM_CARDS];
+CaptureCard cards[NUM_CARDS]; // protected by <bmusb_mutex>
+
+new_frame_ready_callback_t new_frame_ready_callback;
+bool has_new_frame_ready_callback = false;
void bm_frame(int card_index, uint16_t timecode,
FrameAllocator::Frame video_frame, size_t video_offset, uint16_t video_format,
exit(1);
}
- CHECK(init_movit("/usr/share/movit", MOVIT_DEBUG_ON));
+ CHECK(init_movit(MOVIT_SHADER_DIR, MOVIT_DEBUG_OFF));
check_error();
EffectChain chain(WIDTH, HEIGHT);
ycbcr_format.chroma_subsampling_x = 1;
+ chain.add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
chain.add_ycbcr_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED, ycbcr_format, YCBCR_OUTPUT_SPLIT_Y_AND_CBCR);
chain.set_dither_bits(8);
chain.set_output_origin(OUTPUT_ORIGIN_TOP_LEFT);
GLint input_tex_pbo = (GLint)(intptr_t)bmusb_current_rendering_frame[card_index].userdata;
input[card_index]->set_pixel_data(0, nullptr, input_tex_pbo);
input[card_index]->set_pixel_data(1, nullptr, input_tex_pbo);
- check_error();
}
//chain.enable_phase_timing(true);
// Set up stuff for NV12 conversion.
- ResourcePool *resource_pool = chain.get_resource_pool();
+ resource_pool = chain.get_resource_pool();
GLuint chroma_tex = resource_pool->create_2d_texture(GL_RG8, WIDTH, HEIGHT);
// Cb/Cr shader.
float right1 = 1280 - 16;
float top1 = bottom1 - height1;
float left1 = right1 - width1;
-
- float t = 0.5 + 0.5 * cos(frame * 0.006);
- //float t = 0.0;
- float scale0 = 1.0 + t * (1280.0 / 848.0 - 1.0);
- float tx0 = 0.0 + t * (-16.0 * scale0);
- float ty0 = 0.0 + t * (-48.0 * scale0);
-
- top0 = top0 * scale0 + ty0;
- bottom0 = bottom0 * scale0 + ty0;
- left0 = left0 * scale0 + tx0;
- right0 = right0 * scale0 + tx0;
-
- top1 = top1 * scale0 + ty0;
- bottom1 = bottom1 * scale0 + ty0;
- left1 = left1 * scale0 + tx0;
- right1 = right1 * scale0 + tx0;
+
+ if (current_source == SOURCE_INPUT1) {
+ top0 = 0.0;
+ bottom0 = HEIGHT;
+ left0 = 0.0;
+ right0 = WIDTH;
+
+ top1 = HEIGHT + 10;
+ bottom1 = HEIGHT + 20;
+ left1 = WIDTH + 10;
+ right1 = WIDTH + 20;
+ } else if (current_source == SOURCE_INPUT2) {
+ top1 = 0.0;
+ bottom1 = HEIGHT;
+ left1 = 0.0;
+ right1 = WIDTH;
+
+ top0 = HEIGHT + 10;
+ bottom0 = HEIGHT + 20;
+ left0 = WIDTH + 10;
+ right0 = WIDTH + 20;
+ } else {
+ float t = 0.5 + 0.5 * cos(frame * 0.006);
+ float scale0 = 1.0 + t * (1280.0 / 848.0 - 1.0);
+ float tx0 = 0.0 + t * (-16.0 * scale0);
+ float ty0 = 0.0 + t * (-48.0 * scale0);
+
+ top0 = top0 * scale0 + ty0;
+ bottom0 = bottom0 * scale0 + ty0;
+ left0 = left0 * scale0 + tx0;
+ right0 = right0 * scale0 + tx0;
+
+ top1 = top1 * scale0 + ty0;
+ bottom1 = bottom1 * scale0 + ty0;
+ left1 = left1 * scale0 + tx0;
+ right1 = right1 * scale0 + tx0;
+ }
place_rectangle(resample_effect, padding_effect, left0, top0, right0, bottom0);
place_rectangle(resample2_effect, padding2_effect, left1, top1, right1, bottom1);
// knowing when the current one is released.)
input_frames_to_release.push_back(bmusb_current_rendering_frame[card_index]);
bmusb_current_rendering_frame[card_index] = card->new_frame;
+ check_error();
// The new texture might still be uploaded,
// tell the GPU to wait until it's there.
assert(got_frame);
// Render chain.
- {
- GLuint ycbcr_fbo = resource_pool->create_fbo(y_tex, chroma_tex);
- chain.render_to_fbo(ycbcr_fbo, WIDTH, HEIGHT);
- resource_pool->release_fbo(ycbcr_fbo);
- }
+ GLuint rgba_tex = resource_pool->create_2d_texture(GL_RGBA8, WIDTH, HEIGHT);
+ GLuint ycbcr_fbo = resource_pool->create_fbo(y_tex, chroma_tex, rgba_tex);
+ chain.render_to_fbo(ycbcr_fbo, WIDTH, HEIGHT);
+ resource_pool->release_fbo(ycbcr_fbo);
// Set up for extraction.
float vertices[] = {
glBindFramebuffer(GL_FRAMEBUFFER, cbcr_fbo);
glViewport(0, 0, WIDTH/2, HEIGHT/2);
check_error();
- GLsync fence;
- {
- glUseProgram(cbcr_program_num);
- check_error();
- glActiveTexture(GL_TEXTURE0);
- check_error();
- glBindTexture(GL_TEXTURE_2D, chroma_tex);
- check_error();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- check_error();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- check_error();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- check_error();
+ glUseProgram(cbcr_program_num);
+ check_error();
- float chroma_offset_0[] = { -0.5f / WIDTH, 0.0f };
- set_uniform_vec2(cbcr_program_num, "foo", "chroma_offset_0", chroma_offset_0);
+ glActiveTexture(GL_TEXTURE0);
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, chroma_tex);
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ check_error();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ check_error();
- GLuint position_vbo = fill_vertex_attribute(cbcr_program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
- GLuint texcoord_vbo = fill_vertex_attribute(cbcr_program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices); // Same as vertices.
+ float chroma_offset_0[] = { -0.5f / WIDTH, 0.0f };
+ set_uniform_vec2(cbcr_program_num, "foo", "chroma_offset_0", chroma_offset_0);
- glDrawArrays(GL_TRIANGLES, 0, 3);
- check_error();
+ GLuint position_vbo = fill_vertex_attribute(cbcr_program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
+ GLuint texcoord_vbo = fill_vertex_attribute(cbcr_program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices); // Same as vertices.
- cleanup_vertex_attribute(cbcr_program_num, "position", position_vbo);
- cleanup_vertex_attribute(cbcr_program_num, "texcoord", texcoord_vbo);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ check_error();
- glUseProgram(0);
- check_error();
+ RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
+ check_error();
- fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0);
- check_error();
+ cleanup_vertex_attribute(cbcr_program_num, "position", position_vbo);
+ cleanup_vertex_attribute(cbcr_program_num, "texcoord", texcoord_vbo);
- resource_pool->release_fbo(cbcr_fbo);
- }
+ glUseProgram(0);
+ check_error();
+
+ resource_pool->release_fbo(cbcr_fbo);
h264_encoder.end_frame(fence, input_frames_to_release);
+ // Store this frame for display. Remove the ready frame if any
+ // (it was seemingly never used).
+ {
+ std::unique_lock<std::mutex> lock(display_frame_mutex);
+ if (has_ready_display_frame) {
+ resource_pool->release_2d_texture(ready_display_frame.texnum);
+ ready_display_frame.ready_fence.reset();
+ }
+ ready_display_frame.texnum = rgba_tex;
+ ready_display_frame.ready_fence = fence;
+ has_ready_display_frame = true;
+ }
+
+ if (has_new_frame_ready_callback) {
+ new_frame_ready_callback();
+ }
#if 1
#if _POSIX_C_SOURCE >= 199309L
start = now;
}
#endif
+ check_error();
}
glDeleteVertexArrays(1, &vao);
resource_pool->release_glsl_program(cbcr_program_num);
BMUSBCapture::stop_bm_thread();
}
+bool mixer_get_display_frame(DisplayFrame *frame)
+{
+ std::unique_lock<std::mutex> lock(display_frame_mutex);
+ if (!has_current_display_frame && !has_ready_display_frame) {
+ return false;
+ }
+
+ if (has_current_display_frame && has_ready_display_frame) {
+ // We have a new ready frame. Toss the current one.
+ resource_pool->release_2d_texture(current_display_frame.texnum);
+ current_display_frame.ready_fence.reset();
+ has_current_display_frame = false;
+ }
+ if (has_ready_display_frame) {
+ assert(!has_current_display_frame);
+ current_display_frame = ready_display_frame;
+ ready_display_frame.ready_fence.reset(); // Drop the refcount.
+ has_current_display_frame = true;
+ has_ready_display_frame = false;
+ }
+
+ *frame = current_display_frame;
+ return true;
+}
+
+void set_frame_ready_fallback(new_frame_ready_callback_t callback)
+{
+ new_frame_ready_callback = callback;
+ has_new_frame_ready_callback = true;
+}
+
std::thread mixer_thread;
void start_mixer(QSurface *surface, QSurface *surface2, QSurface *surface3, QSurface *surface4)
quit = true;
mixer_thread.join();
}
+
+void mixer_cut(Source source)
+{
+ current_source = source;
+}