Files
StarPilot/frogpilot/ui/screenrecorder/screenrecorder.cc
T
firestar5683 34fb03993a Revert "Plumbology"
This reverts commit 7ed06717d74f6a7033fcf3cd834a712771507d4e.
2025-10-03 00:12:10 -05:00

168 lines
4.8 KiB
C++

#include "libyuv.h"
#include "selfdrive/ui/qt/util.h"
#include "frogpilot/ui/screenrecorder/screenrecorder.h"
constexpr int MAX_DURATION = 1000 * 60 * 5;
constexpr int SCREEN_WIDTH = 2160;
constexpr int SCREEN_HEIGHT = 1080;
const QDir RECORDINGS_FOLDER("/data/media/screen_recordings");
ScreenRecorder::ScreenRecorder(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size, btn_size);
rgbScaleBuffer.resize(SCREEN_WIDTH * SCREEN_HEIGHT * 4);
rootWidget = topWidget(this);
QObject::connect(this, &QPushButton::clicked, this, &ScreenRecorder::toggleRecording);
QObject::connect(uiState(), &UIState::offroadTransition, this, &ScreenRecorder::stopRecording);
QObject::connect(uiState(), &UIState::uiUpdate, this, &ScreenRecorder::updateState);
}
void ScreenRecorder::updateState() {
if (!recording) {
return;
}
if (QDateTime::currentMSecsSinceEpoch() - startedTime > MAX_DURATION) {
stopRecording();
startRecording();
return;
}
if (frameCount % 2 == 0) {
imageQueue.push(rootWidget->grab().toImage());
}
frameCount += 1;
}
void ScreenRecorder::toggleRecording() {
recording ? stopRecording() : startRecording();
}
void ScreenRecorder::startRecording() {
encoder = std::make_unique<OmxEncoder>(RECORDINGS_FOLDER.path().toStdString().c_str(), SCREEN_WIDTH, SCREEN_HEIGHT, UI_FREQ * 2, 12 * 1024 * 1024);
encoder->encoder_open((QDateTime::currentDateTime().toString("MMMM_dd_yyyy-hh-mmAP").toStdString() + ".mp4").c_str());
if (!encoder->is_open) {
encoder.reset();
return;
}
recording = true;
frameCount = 0;
startedTime = QDateTime::currentMSecsSinceEpoch();
encodingThread = std::thread(&ScreenRecorder::encodeImage, this);
}
void ScreenRecorder::stopRecording() {
recording = false;
if (encodingThread.joinable()) {
encodingThread.join();
}
if (encoder) {
encoder->encoder_close();
encoder.reset();
}
}
QImage ScreenRecorder::synthesizeFrame(const QImage &frame1, const QImage &frame2, double alpha) {
QImage blended(frame1.size(), frame1.format());
const uint8_t *bits1 = frame1.constBits();
const uint8_t *bits2 = frame2.constBits();
uint8_t *blendedBits = blended.bits();
int numPixels = frame1.width() * frame1.height();
for (int i = 0; i < numPixels * 4; ++i) {
blendedBits[i] = bits1[i] * (1.0 - alpha) + bits2[i] * alpha;
}
return blended;
}
void ScreenRecorder::encodeImage() {
uint64_t previousTimestamp = 0;
QImage previousImage;
while (recording) {
uint64_t currentTimestamp = nanos_since_boot();
QImage image;
if (imageQueue.pop_wait_for(image, std::chrono::milliseconds(1000 / UI_FREQ))) {
image = image.convertToFormat(QImage::Format_RGBA8888);
if (!previousImage.isNull()) {
double alpha = std::clamp((currentTimestamp - previousTimestamp) / (1000.0 / UI_FREQ), 0.0, 1.0);
QImage syntheticImage = synthesizeFrame(previousImage, image, alpha);
std::copy(syntheticImage.bits(), syntheticImage.bits() + SCREEN_WIDTH * SCREEN_HEIGHT * 4, rgbScaleBuffer.data());
encoder->encode_frame_rgba(rgbScaleBuffer.data(), SCREEN_WIDTH, SCREEN_HEIGHT, (previousTimestamp + currentTimestamp) / 2);
}
std::copy(image.bits(), image.bits() + SCREEN_WIDTH * SCREEN_HEIGHT * 4, rgbScaleBuffer.data());
encoder->encode_frame_rgba(rgbScaleBuffer.data(), SCREEN_WIDTH, SCREEN_HEIGHT, currentTimestamp);
previousImage = image;
previousTimestamp = currentTimestamp;
}
std::this_thread::yield();
}
}
void ScreenRecorder::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
if (recording) {
qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - startedTime;
qreal phase = (elapsed % 2000) / 2000.0 * 2 * M_PI;
qreal alphaFactor = 0.5 + 0.5 * sin(phase);
QColor glowColor = redColor();
glowColor.setAlphaF(0.3 + 0.7 * alphaFactor);
int glowWidth = 8 + static_cast<int>(2 * alphaFactor);
p.setBrush(blackColor(166));
p.setFont(InterFont(25, QFont::Bold));
p.setPen(QPen(glowColor, glowWidth));
} else {
p.setBrush(blackColor(166));
p.setFont(InterFont(25, QFont::DemiBold));
p.setPen(QPen(redColor(), 8));
}
int centeringOffset = 10;
QRect buttonRect(centeringOffset, btn_size / 3, btn_size - centeringOffset * 2, btn_size / 3);
p.drawRoundedRect(buttonRect, 24, 24);
QRect textRect = buttonRect.adjusted(centeringOffset, 0, -centeringOffset, 0);
p.setPen(QPen(whiteColor(), 6));
p.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, recording ? tr("RECORDING") : tr("RECORD"));
if (!recording) {
p.setBrush(redColor(166));
p.setPen(Qt::NoPen);
p.drawEllipse(QPoint(buttonRect.right() - btn_size / 10 - centeringOffset, buttonRect.center().y()), btn_size / 10, btn_size / 10);
}
}