|
|
@ -6,6 +6,14 @@ |
|
|
|
#include <GLES3/gl3.h> |
|
|
|
#include <GLES3/gl3.h> |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <cmath> |
|
|
|
|
|
|
|
#include <set> |
|
|
|
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
#include <utility> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <QOpenGLBuffer> |
|
|
|
|
|
|
|
#include <QOffscreenSurface> |
|
|
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
|
|
const char frame_vertex_shader[] = |
|
|
|
const char frame_vertex_shader[] = |
|
|
@ -87,25 +95,24 @@ mat4 get_fit_view_transform(float widget_aspect_ratio, float frame_aspect_ratio) |
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, bool zoom, QWidget *parent) |
|
|
|
CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, bool zoom, QWidget* parent) : |
|
|
|
: stream_name(stream_name), requested_stream_type(type), zoomed_view(zoom), QOpenGLWidget(parent) { |
|
|
|
stream_name(stream_name), requested_stream_type(type), zoomed_view(zoom), 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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CameraWidget::~CameraWidget() { |
|
|
|
CameraWidget::~CameraWidget() { |
|
|
|
makeCurrent(); |
|
|
|
makeCurrent(); |
|
|
|
|
|
|
|
stopVipcThread(); |
|
|
|
if (isValid()) { |
|
|
|
if (isValid()) { |
|
|
|
glDeleteVertexArrays(1, &frame_vao); |
|
|
|
glDeleteVertexArrays(1, &frame_vao); |
|
|
|
glDeleteBuffers(1, &frame_vbo); |
|
|
|
glDeleteBuffers(1, &frame_vbo); |
|
|
|
glDeleteBuffers(1, &frame_ibo); |
|
|
|
glDeleteBuffers(1, &frame_ibo); |
|
|
|
glDeleteBuffers(2, textures); |
|
|
|
glDeleteBuffers(2, textures); |
|
|
|
} |
|
|
|
} |
|
|
|
#ifdef QCOM2 |
|
|
|
|
|
|
|
EGLDisplay egl_display = eglGetCurrentDisplay(); |
|
|
|
|
|
|
|
for (auto &pair : egl_images) { |
|
|
|
|
|
|
|
eglDestroyImageKHR(egl_display, pair.second); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
egl_images.clear(); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
doneCurrent(); |
|
|
|
doneCurrent(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -169,11 +176,45 @@ void CameraWidget::initializeGL() { |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef QCOM2 |
|
|
|
|
|
|
|
EGLDisplay egl_display = eglGetCurrentDisplay(); |
|
|
|
|
|
|
|
assert(egl_display != EGL_NO_DISPLAY); |
|
|
|
|
|
|
|
for (auto &pair : egl_images) { |
|
|
|
|
|
|
|
eglDestroyImageKHR(egl_display, pair.second); |
|
|
|
|
|
|
|
assert(eglGetError() == EGL_SUCCESS); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
egl_images.clear(); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CameraWidget::availableStreamsUpdated(std::set<VisionStreamType> streams) { |
|
|
|
|
|
|
|
available_streams = streams; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CameraWidget::updateFrameMat() { |
|
|
|
void CameraWidget::updateFrameMat() { |
|
|
|
int w = glWidth(), h = glHeight(); |
|
|
|
int w = glWidth(), h = glHeight(); |
|
|
|
|
|
|
|
|
|
|
|
if (zoomed_view) { |
|
|
|
if (zoomed_view) { |
|
|
|
if (streamType() == VISION_STREAM_DRIVER) { |
|
|
|
if (active_stream_type == VISION_STREAM_DRIVER) { |
|
|
|
if (stream_width > 0 && stream_height > 0) { |
|
|
|
if (stream_width > 0 && stream_height > 0) { |
|
|
|
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height); |
|
|
|
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height); |
|
|
|
} |
|
|
|
} |
|
|
@ -182,7 +223,7 @@ void CameraWidget::updateFrameMat() { |
|
|
|
// to ensure this ends up in the middle of the screen
|
|
|
|
// to ensure this ends up in the middle of the screen
|
|
|
|
// for narrow come and a little lower for wide cam.
|
|
|
|
// for narrow come and a little lower for wide cam.
|
|
|
|
// TODO: use proper perspective transform?
|
|
|
|
// TODO: use proper perspective transform?
|
|
|
|
if (streamType() == VISION_STREAM_WIDE_ROAD) { |
|
|
|
if (active_stream_type == VISION_STREAM_WIDE_ROAD) { |
|
|
|
intrinsic_matrix = ECAM_INTRINSIC_MATRIX; |
|
|
|
intrinsic_matrix = ECAM_INTRINSIC_MATRIX; |
|
|
|
zoom = 2.0; |
|
|
|
zoom = 2.0; |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -228,15 +269,25 @@ void CameraWidget::paintGL() { |
|
|
|
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); |
|
|
|
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); |
|
|
|
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
|
|
|
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
|
|
|
|
|
|
|
|
|
|
|
if (!frame) return; |
|
|
|
std::lock_guard lk(frame_lock); |
|
|
|
|
|
|
|
if (frames.empty()) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int frame_idx = frames.size() - 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Always draw latest frame until sync logic is more stable
|
|
|
|
|
|
|
|
// for (frame_idx = 0; frame_idx < frames.size() - 1; frame_idx++) {
|
|
|
|
|
|
|
|
// if (frames[frame_idx].first == draw_frame_id) break;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// Log duplicate/dropped frames
|
|
|
|
// Log duplicate/dropped frames
|
|
|
|
if (frame_id == prev_frame_id) { |
|
|
|
if (frames[frame_idx].first == prev_frame_id) { |
|
|
|
qDebug() << "Drawing same frame twice" << frame_id; |
|
|
|
qDebug() << "Drawing same frame twice" << frames[frame_idx].first; |
|
|
|
} else if (frame_id != prev_frame_id + 1) { |
|
|
|
} else if (frames[frame_idx].first != prev_frame_id + 1) { |
|
|
|
qDebug() << "Skipped frame" << frame_id; |
|
|
|
qDebug() << "Skipped frame" << frames[frame_idx].first; |
|
|
|
} |
|
|
|
} |
|
|
|
prev_frame_id = frame_id; |
|
|
|
prev_frame_id = frames[frame_idx].first; |
|
|
|
|
|
|
|
VisionBuf *frame = frames[frame_idx].second; |
|
|
|
|
|
|
|
assert(frame != nullptr); |
|
|
|
|
|
|
|
|
|
|
|
updateFrameMat(); |
|
|
|
updateFrameMat(); |
|
|
|
|
|
|
|
|
|
|
@ -276,7 +327,7 @@ void CameraWidget::paintGL() { |
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CameraWidget::vipcConnected() { |
|
|
|
void CameraWidget::vipcConnected(VisionIpcClient *vipc_client) { |
|
|
|
makeCurrent(); |
|
|
|
makeCurrent(); |
|
|
|
stream_width = vipc_client->buffers[0].width; |
|
|
|
stream_width = vipc_client->buffers[0].width; |
|
|
|
stream_height = vipc_client->buffers[0].height; |
|
|
|
stream_height = vipc_client->buffers[0].height; |
|
|
@ -326,69 +377,59 @@ void CameraWidget::vipcConnected() { |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CameraWidget::receiveFrame(uint64_t request_frame_id) { |
|
|
|
void CameraWidget::vipcFrameReceived() { |
|
|
|
if (!vipc_client || vipc_client->type != requested_stream_type) { |
|
|
|
update(); |
|
|
|
qDebug().nospace() << "connecting to stream" << requested_stream_type |
|
|
|
} |
|
|
|
<< (vipc_client ? QString(", was connected to %1").arg(vipc_client->type) : ""); |
|
|
|
|
|
|
|
vipc_client.reset(new VisionIpcClient(stream_name, requested_stream_type, false)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!vipc_client->connected) { |
|
|
|
void CameraWidget::vipcThread() { |
|
|
|
frame = nullptr; |
|
|
|
VisionStreamType cur_stream = requested_stream_type; |
|
|
|
recent_frames.clear(); |
|
|
|
std::unique_ptr<VisionIpcClient> vipc_client; |
|
|
|
available_streams = VisionIpcClient::getAvailableStreams(stream_name, false); |
|
|
|
VisionIpcBufExtra meta_main = {0}; |
|
|
|
if (available_streams.empty() || !vipc_client->connect(false)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
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)); |
|
|
|
} |
|
|
|
} |
|
|
|
emit vipcAvailableStreamsUpdated(); |
|
|
|
active_stream_type = cur_stream; |
|
|
|
vipcConnected(); |
|
|
|
|
|
|
|
} |
|
|
|
if (!vipc_client->connected) { |
|
|
|
|
|
|
|
clearFrames(); |
|
|
|
|
|
|
|
auto streams = VisionIpcClient::getAvailableStreams(stream_name, false); |
|
|
|
|
|
|
|
if (streams.empty()) { |
|
|
|
|
|
|
|
QThread::msleep(100); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
emit vipcAvailableStreamsUpdated(streams); |
|
|
|
|
|
|
|
|
|
|
|
VisionIpcBufExtra meta_main = {}; |
|
|
|
if (!vipc_client->connect(false)) { |
|
|
|
while (auto buf = vipc_client->recv(&meta_main, 0)) { |
|
|
|
QThread::msleep(100); |
|
|
|
if (recent_frames.size() > FRAME_BUFFER_SIZE) { |
|
|
|
continue; |
|
|
|
recent_frames.pop_front(); |
|
|
|
} |
|
|
|
|
|
|
|
emit vipcThreadConnected(vipc_client.get()); |
|
|
|
} |
|
|
|
} |
|
|
|
recent_frames.emplace_back(meta_main.frame_id, buf); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frame = nullptr; |
|
|
|
if (VisionBuf *buf = vipc_client->recv(&meta_main, 1000)) { |
|
|
|
if (request_frame_id > 0) { |
|
|
|
{ |
|
|
|
auto it = std::find_if(recent_frames.begin(), recent_frames.end(), |
|
|
|
std::lock_guard lk(frame_lock); |
|
|
|
[request_frame_id](auto &f) { return f.first == request_frame_id; }); |
|
|
|
frames.push_back(std::make_pair(meta_main.frame_id, buf)); |
|
|
|
if (it != recent_frames.end()) { |
|
|
|
while (frames.size() > FRAME_BUFFER_SIZE) { |
|
|
|
std::tie(frame_id, frame) = *it; |
|
|
|
frames.pop_front(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
emit vipcThreadFrameReceived(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (!isVisible()) { |
|
|
|
|
|
|
|
vipc_client->connected = false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (!recent_frames.empty()) { |
|
|
|
|
|
|
|
std::tie(frame_id, frame) = recent_frames.back(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return frame != nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CameraWidget::disconnectVipc() { |
|
|
|
|
|
|
|
recent_frames.clear(); |
|
|
|
|
|
|
|
frame = nullptr; |
|
|
|
|
|
|
|
frame_id = 0; |
|
|
|
|
|
|
|
prev_frame_id = 0; |
|
|
|
|
|
|
|
if (vipc_client) { |
|
|
|
|
|
|
|
vipc_client->connected = false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Cameraview
|
|
|
|
void CameraWidget::clearFrames() { |
|
|
|
|
|
|
|
std::lock_guard lk(frame_lock); |
|
|
|
CameraView::CameraView(const std::string &name, VisionStreamType stream_type, bool zoom, QWidget *parent) |
|
|
|
frames.clear(); |
|
|
|
: CameraWidget(name, stream_type, zoom, parent) { |
|
|
|
available_streams.clear(); |
|
|
|
timer = new QTimer(this); |
|
|
|
|
|
|
|
timer->setInterval(1000.0 / UI_FREQ); |
|
|
|
|
|
|
|
timer->callOnTimeout(this, [this]() { if (receiveFrame()) update(); }); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CameraView::showEvent(QShowEvent *event) { |
|
|
|
|
|
|
|
timer->start(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CameraView::hideEvent(QHideEvent *event) { |
|
|
|
|
|
|
|
timer->stop(); |
|
|
|
|
|
|
|
disconnectVipc(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|