slightly better, skips occasional frames but not horrid

pull/34892/head
Trey Moen 3 months ago
parent 4e0a8f3094
commit 282cebc71b
  1. 58
      tools/clip/application.cc
  2. 1
      tools/clip/application.h
  3. 24
      tools/clip/recorder/widget.cc
  4. 3
      tools/clip/recorder/widget.h

@ -2,6 +2,7 @@
#include <QApplication>
#include <QTranslator>
#include <QWindow>
#include <selfdrive/ui/qt/util.h>
#include <selfdrive/ui/qt/window.h>
@ -73,30 +74,54 @@ Application::Application(int argc, char *argv[], QObject *parent) : QObject(pare
recorder->moveToThread(recorderThread);
connect(recorderThread, &QThread::finished, recorder, &QObject::deleteLater);
connect(app, &QCoreApplication::aboutToQuit, recorderThread, &QThread::quit);
recorderThread->start();
// Initialize and start replay
initReplay(route.toStdString());
replayThread = QThread::create([this, startTime] { startReplay(startTime); });
replayThread->start();
QTimer *loop = new QTimer;
connect(loop, &QTimer::timeout, this, [&]() {
// Frame capture optimization
QElapsedTimer frameTimer;
frameTimer.start();
int64_t lastFrameTime = 0;
const int64_t frameInterval = 1000 / UI_FREQ; // Target frame interval in ms
loop = new QTimer;
connect(loop, &QTimer::timeout, this, [&, frameTimer, lastFrameTime]() mutable {
if (!window->isVisible()) {
return;
}
QElapsedTimer timer;
timer.start();
int64_t currentTime = frameTimer.elapsed();
int64_t elapsedSinceLastFrame = currentTime - lastFrameTime;
// Skip frame if we're ahead of schedule
if (elapsedSinceLastFrame < frameInterval) {
return;
}
QPixmap pixmap = window->grab();
qDebug() << "pixmap took " << timer.elapsed() << " ms";
timer.restart();
recorder->saveFrame(std::make_shared<QPixmap>(std::move(pixmap)));
// Only process frame if capture was successful
if (!pixmap.isNull()) {
recorder->saveFrame(std::make_shared<QPixmap>(std::move(pixmap)));
lastFrameTime = currentTime;
}
});
loop->start(1000 / UI_FREQ);
// Use a higher timer resolution for more precise frame timing
loop->setTimerType(Qt::PreciseTimer);
loop->start(1); // Run at highest possible frequency, we'll control frame rate ourselves
window->setAttribute(Qt::WA_DontShowOnScreen);
window->setAttribute(Qt::WA_OpaquePaintEvent);
window->setAttribute(Qt::WA_NoSystemBackground);
recorderThread->start();
// Initialize and start replay
initReplay(route.toStdString());
replayThread = QThread::create([this, startTime] { startReplay(startTime); });
replayThread->start();
window->setAttribute(Qt::WA_TranslucentBackground, false);
window->setAttribute(Qt::WA_AlwaysStackOnTop);
window->setAttribute(Qt::WA_ShowWithoutActivating);
window->setAttribute(Qt::WA_UpdatesDisabled);
window->setAttribute(Qt::WA_StaticContents);
}
void Application::initReplay(const std::string& route) {
@ -132,15 +157,18 @@ Application::~Application() {
if (recorderThread) {
recorderThread->quit();
recorderThread->wait();
delete recorderThread;
}
delete recorder;
delete window;
delete loop;
delete app;
}
int Application::exec() const {
// TODO: modify Replay to block until all OnroadWindow required messages have been broadcast at least once
std::this_thread::sleep_for(std::chrono::seconds(5));
std::this_thread::sleep_for(std::chrono::seconds(8));
setMainWindow(window);
return app->exec();
}

@ -22,6 +22,7 @@ private:
QThread *recorderThread = nullptr;
Recorder *recorder = nullptr;
OnroadWindow *window;
QTimer *loop;
// Replay related members
std::unique_ptr<Replay> replay;

@ -3,37 +3,47 @@
#include "tools/clip/recorder/ffmpeg.h"
Recorder::Recorder(const std::string& outputFile, QObject *parent) : QObject(parent) {
encoder = new FFmpegEncoder(outputFile, DEVICE_SCREEN_SIZE.width(), DEVICE_SCREEN_SIZE.height(), UI_FREQ);
const float scale = util::getenv("SCALE", 1.0f);
encoder = new FFmpegEncoder(outputFile, DEVICE_SCREEN_SIZE.width() * scale, DEVICE_SCREEN_SIZE.height() * scale, UI_FREQ);
}
Recorder::~Recorder() {
keepRunning = false; // Signal processing thread to stop
delete encoder;
}
void Recorder::saveFrame(const std::shared_ptr<QPixmap> &frame) {
QMutexLocker locker(&mutex);
// Drop frame if queue is full
if (frameQueue.size() >= MAX_QUEUE_SIZE) {
qDebug() << "Dropping frame";
return;
}
frameQueue.enqueue(frame);
if (!isProcessing) {
isProcessing = true;
if (isProcessing.loadRelaxed() == 0) {
isProcessing.storeRelaxed(1);
QMetaObject::invokeMethod(this, &Recorder::processQueue, Qt::QueuedConnection);
}
}
void Recorder::processQueue() {
while (true) {
while (keepRunning) {
std::shared_ptr<QPixmap> frame;
{
QMutexLocker locker(&mutex);
if (frameQueue.isEmpty() || !keepRunning) {
isProcessing = false;
if (frameQueue.isEmpty()) {
isProcessing.storeRelaxed(0);
return;
}
frame = frameQueue.dequeue();
}
if (!encoder->writeFrame(frame->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied))) {
if (!encoder->writeFrame(frame->toImage().convertToFormat(QImage::Format_ARGB32))) {
fprintf(stderr, "did not write\n");
}
}
isProcessing.storeRelaxed(0);
}

@ -19,10 +19,11 @@ public slots:
void saveFrame(const std::shared_ptr<QPixmap> &frame);
private:
static constexpr int MAX_QUEUE_SIZE = 30; // Limit queue size to prevent memory growth
FFmpegEncoder *encoder;
QQueue<std::shared_ptr<QPixmap>> frameQueue;
QMutex mutex;
bool isProcessing = false;
QAtomicInt isProcessing{0}; // Use atomic for thread safety
bool keepRunning = true;
void processQueue();
};

Loading…
Cancel
Save