parent
45ea4a1f75
commit
631a067257
4 changed files with 328 additions and 2 deletions
@ -0,0 +1,261 @@ |
||||
#include "tools/cabana/cameraview.h" |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenGL/gl3.h> |
||||
#else |
||||
#include <GLES3/gl3.h> |
||||
#endif |
||||
|
||||
#include <QApplication> |
||||
|
||||
namespace { |
||||
|
||||
const char frame_vertex_shader[] = |
||||
#ifdef __APPLE__ |
||||
"#version 330 core\n" |
||||
#else |
||||
"#version 300 es\n" |
||||
#endif |
||||
"layout(location = 0) in vec4 aPosition;\n" |
||||
"layout(location = 1) in vec2 aTexCoord;\n" |
||||
"uniform mat4 uTransform;\n" |
||||
"out vec2 vTexCoord;\n" |
||||
"void main() {\n" |
||||
" gl_Position = uTransform * aPosition;\n" |
||||
" vTexCoord = aTexCoord;\n" |
||||
"}\n"; |
||||
|
||||
const char frame_fragment_shader[] = |
||||
#ifdef __APPLE__ |
||||
"#version 330 core\n" |
||||
#else |
||||
"#version 300 es\n" |
||||
"precision mediump float;\n" |
||||
#endif |
||||
"uniform sampler2D uTextureY;\n" |
||||
"uniform sampler2D uTextureUV;\n" |
||||
"in vec2 vTexCoord;\n" |
||||
"out vec4 colorOut;\n" |
||||
"void main() {\n" |
||||
" float y = texture(uTextureY, vTexCoord).r;\n" |
||||
" vec2 uv = texture(uTextureUV, vTexCoord).rg - 0.5;\n" |
||||
" float r = y + 1.402 * uv.y;\n" |
||||
" float g = y - 0.344 * uv.x - 0.714 * uv.y;\n" |
||||
" float b = y + 1.772 * uv.x;\n" |
||||
" colorOut = vec4(r, g, b, 1.0);\n" |
||||
"}\n"; |
||||
|
||||
} // namespace
|
||||
|
||||
CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, QWidget* parent) : |
||||
stream_name(stream_name), active_stream_type(type), requested_stream_type(type), QOpenGLWidget(parent) { |
||||
setAttribute(Qt::WA_OpaquePaintEvent); |
||||
qRegisterMetaType<std::set<VisionStreamType>>("availableStreams"); |
||||
QObject::connect(this, &CameraWidget::vipcThreadConnected, this, &CameraWidget::vipcConnected, Qt::BlockingQueuedConnection); |
||||
QObject::connect(this, &CameraWidget::vipcThreadFrameReceived, this, &CameraWidget::vipcFrameReceived, Qt::QueuedConnection); |
||||
QObject::connect(this, &CameraWidget::vipcAvailableStreamsUpdated, this, &CameraWidget::availableStreamsUpdated, Qt::QueuedConnection); |
||||
QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, this, &CameraWidget::stopVipcThread); |
||||
} |
||||
|
||||
CameraWidget::~CameraWidget() { |
||||
makeCurrent(); |
||||
stopVipcThread(); |
||||
if (isValid()) { |
||||
glDeleteVertexArrays(1, &frame_vao); |
||||
glDeleteBuffers(1, &frame_vbo); |
||||
glDeleteBuffers(1, &frame_ibo); |
||||
glDeleteTextures(2, textures); |
||||
shader_program_.reset(); |
||||
} |
||||
doneCurrent(); |
||||
} |
||||
|
||||
void CameraWidget::initializeGL() { |
||||
initializeOpenGLFunctions(); |
||||
|
||||
shader_program_ = std::make_unique<QOpenGLShaderProgram>(context()); |
||||
shader_program_->addShaderFromSourceCode(QOpenGLShader::Vertex, frame_vertex_shader); |
||||
shader_program_->addShaderFromSourceCode(QOpenGLShader::Fragment, frame_fragment_shader); |
||||
shader_program_->link(); |
||||
|
||||
GLint frame_pos_loc = shader_program_->attributeLocation("aPosition"); |
||||
GLint frame_texcoord_loc = shader_program_->attributeLocation("aTexCoord"); |
||||
|
||||
auto [x1, x2, y1, y2] = requested_stream_type == VISION_STREAM_DRIVER ? std::tuple(0.f, 1.f, 1.f, 0.f) : std::tuple(1.f, 0.f, 1.f, 0.f); |
||||
const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3}; |
||||
const float frame_coords[4][4] = { |
||||
{-1.0, -1.0, x2, y1}, // bl
|
||||
{-1.0, 1.0, x2, y2}, // tl
|
||||
{ 1.0, 1.0, x1, y2}, // tr
|
||||
{ 1.0, -1.0, x1, y1}, // br
|
||||
}; |
||||
|
||||
glGenVertexArrays(1, &frame_vao); |
||||
glBindVertexArray(frame_vao); |
||||
glGenBuffers(1, &frame_vbo); |
||||
glBindBuffer(GL_ARRAY_BUFFER, frame_vbo); |
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW); |
||||
glEnableVertexAttribArray(frame_pos_loc); |
||||
glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE, |
||||
sizeof(frame_coords[0]), (const void *)0); |
||||
glEnableVertexAttribArray(frame_texcoord_loc); |
||||
glVertexAttribPointer(frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE, |
||||
sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2)); |
||||
glGenBuffers(1, &frame_ibo); |
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frame_ibo); |
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW); |
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||
glBindVertexArray(0); |
||||
|
||||
glGenTextures(2, textures); |
||||
|
||||
shader_program_->bind(); |
||||
shader_program_->setUniformValue("uTextureY", 0); |
||||
shader_program_->setUniformValue("uTextureUV", 1); |
||||
shader_program_->release(); |
||||
} |
||||
|
||||
void CameraWidget::showEvent(QShowEvent *event) { |
||||
if (!vipc_thread) { |
||||
clearFrames(); |
||||
vipc_thread = new QThread(); |
||||
connect(vipc_thread, &QThread::started, [=]() { vipcThread(); }); |
||||
connect(vipc_thread, &QThread::finished, vipc_thread, &QObject::deleteLater); |
||||
vipc_thread->start(); |
||||
} |
||||
} |
||||
|
||||
void CameraWidget::stopVipcThread() { |
||||
makeCurrent(); |
||||
if (vipc_thread) { |
||||
vipc_thread->requestInterruption(); |
||||
vipc_thread->quit(); |
||||
vipc_thread->wait(); |
||||
vipc_thread = nullptr; |
||||
} |
||||
} |
||||
|
||||
void CameraWidget::availableStreamsUpdated(std::set<VisionStreamType> streams) { |
||||
available_streams = streams; |
||||
} |
||||
|
||||
void CameraWidget::paintGL() { |
||||
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); |
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
||||
|
||||
std::lock_guard lk(frame_lock); |
||||
if (!current_frame_) return; |
||||
|
||||
// Scale for aspect ratio
|
||||
float widget_ratio = (float)width() / height(); |
||||
float frame_ratio = (float)stream_width / stream_height; |
||||
float scale_x = std::min(frame_ratio / widget_ratio, 1.0f); |
||||
float scale_y = std::min(widget_ratio / frame_ratio, 1.0f); |
||||
|
||||
glViewport(0, 0, width() * devicePixelRatio(), height() * devicePixelRatio()); |
||||
|
||||
shader_program_->bind(); |
||||
QMatrix4x4 transform; |
||||
transform.scale(scale_x, scale_y, 1.0f); |
||||
shader_program_->setUniformValue("uTransform", transform); |
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride); |
||||
glActiveTexture(GL_TEXTURE0); |
||||
glBindTexture(GL_TEXTURE_2D, textures[0]); |
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width, stream_height, GL_RED, GL_UNSIGNED_BYTE, current_frame_->y); |
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride/2); |
||||
glActiveTexture(GL_TEXTURE1); |
||||
glBindTexture(GL_TEXTURE_2D, textures[1]); |
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width/2, stream_height/2, GL_RG, GL_UNSIGNED_BYTE, current_frame_->uv); |
||||
|
||||
glBindVertexArray(frame_vao); |
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, nullptr); |
||||
glBindVertexArray(0); |
||||
|
||||
// Reset both texture units
|
||||
glActiveTexture(GL_TEXTURE1); |
||||
glBindTexture(GL_TEXTURE_2D, 0); |
||||
glActiveTexture(GL_TEXTURE0); |
||||
glBindTexture(GL_TEXTURE_2D, 0); |
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); |
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
||||
|
||||
shader_program_->release(); |
||||
} |
||||
|
||||
void CameraWidget::vipcConnected(VisionIpcClient *vipc_client) { |
||||
makeCurrent(); |
||||
stream_width = vipc_client->buffers[0].width; |
||||
stream_height = vipc_client->buffers[0].height; |
||||
stream_stride = vipc_client->buffers[0].stride; |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, stream_width, stream_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textures[1]); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, stream_width/2, stream_height/2, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
||||
void CameraWidget::vipcFrameReceived() { |
||||
update(); |
||||
} |
||||
|
||||
void CameraWidget::vipcThread() { |
||||
VisionStreamType cur_stream = requested_stream_type; |
||||
std::unique_ptr<VisionIpcClient> vipc_client; |
||||
VisionIpcBufExtra frame_meta = {}; |
||||
|
||||
while (!QThread::currentThread()->isInterruptionRequested()) { |
||||
if (!vipc_client || cur_stream != requested_stream_type) { |
||||
clearFrames(); |
||||
qDebug().nospace() << "connecting to stream " << requested_stream_type << ", was connected to " << cur_stream; |
||||
cur_stream = requested_stream_type; |
||||
vipc_client.reset(new VisionIpcClient(stream_name, cur_stream, false)); |
||||
} |
||||
active_stream_type = cur_stream; |
||||
|
||||
if (!vipc_client->connected) { |
||||
clearFrames(); |
||||
auto streams = VisionIpcClient::getAvailableStreams(stream_name, false); |
||||
if (streams.empty()) { |
||||
QThread::msleep(100); |
||||
continue; |
||||
} |
||||
emit vipcAvailableStreamsUpdated(streams); |
||||
|
||||
if (!vipc_client->connect(false)) { |
||||
QThread::msleep(100); |
||||
continue; |
||||
} |
||||
emit vipcThreadConnected(vipc_client.get()); |
||||
} |
||||
|
||||
if (VisionBuf *buf = vipc_client->recv(&frame_meta, 100)) { |
||||
{ |
||||
std::lock_guard lk(frame_lock); |
||||
current_frame_ = buf; |
||||
frame_meta_ = frame_meta; |
||||
} |
||||
emit vipcThreadFrameReceived(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CameraWidget::clearFrames() { |
||||
std::lock_guard lk(frame_lock); |
||||
current_frame_ = nullptr; |
||||
available_streams.clear(); |
||||
} |
@ -0,0 +1,64 @@ |
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
#include <mutex> |
||||
#include <set> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include <QOpenGLFunctions> |
||||
#include <QOpenGLShaderProgram> |
||||
#include <QOpenGLWidget> |
||||
#include <QThread> |
||||
|
||||
#include "msgq/visionipc/visionipc_client.h" |
||||
|
||||
class CameraWidget : public QOpenGLWidget, protected QOpenGLFunctions { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
using QOpenGLWidget::QOpenGLWidget; |
||||
explicit CameraWidget(std::string stream_name, VisionStreamType stream_type, QWidget* parent = nullptr); |
||||
~CameraWidget(); |
||||
void setStreamType(VisionStreamType type) { requested_stream_type = type; } |
||||
VisionStreamType getStreamType() { return active_stream_type; } |
||||
void stopVipcThread(); |
||||
|
||||
signals: |
||||
void clicked(); |
||||
void vipcThreadConnected(VisionIpcClient *); |
||||
void vipcThreadFrameReceived(); |
||||
void vipcAvailableStreamsUpdated(std::set<VisionStreamType>); |
||||
|
||||
protected: |
||||
void paintGL() override; |
||||
void initializeGL() override; |
||||
void showEvent(QShowEvent *event) override; |
||||
void mouseReleaseEvent(QMouseEvent *event) override { emit clicked(); } |
||||
void vipcThread(); |
||||
void clearFrames(); |
||||
|
||||
GLuint frame_vao, frame_vbo, frame_ibo; |
||||
GLuint textures[2]; |
||||
std::unique_ptr<QOpenGLShaderProgram> shader_program_; |
||||
QColor bg = Qt::black; |
||||
|
||||
std::string stream_name; |
||||
int stream_width = 0; |
||||
int stream_height = 0; |
||||
int stream_stride = 0; |
||||
std::atomic<VisionStreamType> active_stream_type; |
||||
std::atomic<VisionStreamType> requested_stream_type; |
||||
std::set<VisionStreamType> available_streams; |
||||
QThread *vipc_thread = nullptr; |
||||
std::recursive_mutex frame_lock; |
||||
VisionBuf* current_frame_ = nullptr; |
||||
VisionIpcBufExtra frame_meta_ = {}; |
||||
|
||||
protected slots: |
||||
void vipcConnected(VisionIpcClient *vipc_client); |
||||
void vipcFrameReceived(); |
||||
void availableStreamsUpdated(std::set<VisionStreamType> streams); |
||||
}; |
||||
|
||||
Q_DECLARE_METATYPE(std::set<VisionStreamType>); |
Loading…
Reference in new issue