#include <QThread>
#include <math.h>
#include <thread>
+#include <movit/resource_pool.h>
+#undef Success
+#include <movit/util.h>
GLWidget::GLWidget(QWidget *parent)
- : QOpenGLWidget(parent)
+ : QOpenGLWidget(parent),
+ resource_pool(new movit::ResourcePool)
{
}
printf("egl context=%p\n", eglGetCurrentContext());
//printf("threads: %p %p\n", QThread::currentThread(), qGuiApp->thread());
+ GLWidget *t = this;
+ set_frame_ready_fallback([t]{
+ QMetaObject::invokeMethod(t, "update", Qt::AutoConnection);
+ });
+
QSurface *surface = create_surface(format());
QSurface *surface2 = create_surface(format());
QSurface *surface3 = create_surface(format());
QSurface *surface4 = create_surface(format());
start_mixer(surface, surface2, surface3, surface4);
+
+ // Prepare the shaders to actually get the texture shown (ick).
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+
+ std::string vert_shader =
+ "#version 130 \n"
+ "in vec2 position; \n"
+ "in vec2 texcoord; \n"
+ "out vec2 tc; \n"
+ " \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); \n"
+ " tc = texcoord; \n"
+ " tc.y = 1.0 - tc.y; \n"
+ "} \n";
+ std::string frag_shader =
+ "#version 130 \n"
+ "in vec2 tc; \n"
+ "uniform sampler2D tex; \n"
+ "void main() { \n"
+ " gl_FragColor = texture2D(tex, tc); \n"
+ "} \n";
+ program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
+
+ static const float vertices[] = {
+ 0.0f, 2.0f,
+ 0.0f, 0.0f,
+ 2.0f, 0.0f
+ };
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+ GLuint position_vbo = movit::fill_vertex_attribute(program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
+ GLuint texcoord_vbo = movit::fill_vertex_attribute(program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices); // Same as vertices.
+
+#if 0
+ // Cleanup.
+ cleanup_vertex_attribute(phases[0]->glsl_program_num, "position", position_vbo);
+ cleanup_vertex_attribute(phases[0]->glsl_program_num, "texcoord", texcoord_vbo);
+
+ glDeleteVertexArrays(1, &vao);
+ nheck_error();
+#endif
}
void GLWidget::paintGL()
{
- glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ DisplayFrame frame;
+ if (!mixer_get_display_frame(&frame)) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ return;
+ }
+
+ glUseProgram(program_num);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, frame.texnum);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindVertexArray(vao);
+ glWaitSync(frame.ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
}
#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;
+static float t = 0.0f;
-std::mutex bmusb_mutex; // protects <cards>
+ResourcePool *resource_pool;
+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,
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);
//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.
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[] = {
glDrawArrays(GL_TRIANGLES, 0, 3);
check_error();
+ RefCountedGLsync fence(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);
glUseProgram(0);
check_error();
- RefCountedGLsync fence(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/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
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)