* initial commit, works

* remove nui

* working again

* visionipc

* cleanup

* cleanup

* moving VisionIpcServer to Unlogger class

* works

* tab cleanup

* headless mode

* headless mode works

* working headless mode

* gitignore update

* small unlogger refactor

* refactor param in UIState

* works, very slow, hacks

* cleanup

* works

* cleanup

* cleanup

* unused

* works for whole route

* nicer

* a little nicer

* different threshold

* maintains 1 segment window

* works with public api

* comments

* networkTimer works

* cleanup

* unified HttpRequest

* tabs

* tabs

* comments'

* gitignore

* gitignore

* only on PC

* same line else

* no changes in home.cc

* scons

* update scons

* works

* revert mainc.c

* revert home

* else

* just api + problem with api send

* works

* include cleanup

* general json fail

* whitespace

* remove active

* adding request repeater

* removing comments

* tabs

* update comment

* cereal

* fix

* trailing new lines

* grammar

* if whitespace

* indentation

* Update selfdrive/ui/SConscript

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>

* Update selfdrive/ui/qt/request_repeater.cc

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>

* works

* sort by dir

* no blockSignal

* replay is now QOBject

* cant take const char

* rename inner it

* get width and height from frame readeR

* resolve TODO

* seek in next pr

* spaces

* ui stuff

* fix CI

* remove comments

* no repalce

* trim segment fix

* remove seek from stream

* no cache key

* final changes'

* fix

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
old-commit-hash: 19d962cdf37b80523deba6518057f2e860f65fee
This commit is contained in:
iejMac
2021-04-24 00:59:09 -07:00
committed by GitHub
parent fbe27337ca
commit ff2138d905
26 changed files with 266 additions and 426 deletions
+5 -3
View File
@@ -30,15 +30,17 @@ public:
int getRGBSize() { return width*height*3; }
void loaderThread();
void cacherThread();
//TODO: get this from the actual frame
int width = 1164;
int height = 874;
private:
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
struct SwsContext *sws_ctx = NULL;
int width = 1164;
int height = 874;
std::vector<AVPacket *> pkts;
std::thread *t;
-8
View File
@@ -1,8 +0,0 @@
.*.swp
*.o
_nui
moc_*
nui.app/*
routes.json
_nui.app
-143
View File
@@ -1,143 +0,0 @@
#include "FileReader.hpp"
#include "FrameReader.hpp"
#include <QtNetwork>
FileReader::FileReader(const QString& file_) : file(file_) {
}
void FileReader::process() {
timer.start();
QString str = file.simplified();
str.replace(" ", "");
startRequest(QUrl(str));
}
void FileReader::startRequest(const QUrl &url) {
qnam = new QNetworkAccessManager;
reply = qnam->get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, &FileReader::httpFinished);
connect(reply, &QIODevice::readyRead, this, &FileReader::readyRead);
qDebug() << "requesting" << url;
}
void FileReader::httpFinished() {
if (reply->error()) {
qWarning() << reply->errorString();
}
const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redirectionTarget.isNull()) {
const QUrl redirectedUrl = redirectionTarget.toUrl();
//qDebug() << "redirected to" << redirectedUrl;
startRequest(redirectedUrl);
} else {
qDebug() << "done in" << timer.elapsed() << "ms";
done();
}
}
void FileReader::readyRead() {
QByteArray dat = reply->readAll();
printf("got http ready read: %d\n", dat.size());
}
FileReader::~FileReader() {
}
LogReader::LogReader(const QString& file, Events *events_, QReadWriteLock* events_lock_, QMap<int, QPair<int, int> > *eidx_) :
FileReader(file), events(events_), events_lock(events_lock_), eidx(eidx_) {
bStream.next_in = NULL;
bStream.avail_in = 0;
bStream.bzalloc = NULL;
bStream.bzfree = NULL;
bStream.opaque = NULL;
int ret = BZ2_bzDecompressInit(&bStream, 0, 0);
if (ret != BZ_OK) qWarning() << "bz2 init failed";
// start with 64MB buffer
raw.resize(1024*1024*64);
// auto increment?
bStream.next_out = raw.data();
bStream.avail_out = raw.size();
// parsed no events yet
event_offset = 0;
parser = new std::thread([&]() {
while (1) {
mergeEvents(cdled.get());
}
});
}
LogReader::~LogReader() {
delete parser;
}
void LogReader::mergeEvents(int dled) {
auto amsg = kj::arrayPtr((const capnp::word*)(raw.data() + event_offset), (dled-event_offset)/sizeof(capnp::word));
Events events_local;
QMap<int, QPair<int, int> > eidx_local;
while (amsg.size() > 0) {
try {
capnp::FlatArrayMessageReader cmsg = capnp::FlatArrayMessageReader(amsg);
// this needed? it is
capnp::FlatArrayMessageReader *tmsg =
new capnp::FlatArrayMessageReader(kj::arrayPtr(amsg.begin(), cmsg.getEnd()));
amsg = kj::arrayPtr(cmsg.getEnd(), amsg.end());
cereal::Event::Reader event = tmsg->getRoot<cereal::Event>();
events_local.insert(event.getLogMonoTime(), event);
// hack
// TODO: rewrite with callback
if (event.which() == cereal::Event::ROAD_ENCODE_IDX) {
auto ee = event.getRoadEncodeIdx();
eidx_local.insert(ee.getFrameId(), qMakePair(ee.getSegmentNum(), ee.getSegmentId()));
}
// increment
event_offset = (char*)cmsg.getEnd() - raw.data();
} catch (const kj::Exception& e) {
// partial messages trigger this
//qDebug() << e.getDescription().cStr();
break;
}
}
// merge in events
// TODO: add lock
events_lock->lockForWrite();
*events += events_local;
eidx->unite(eidx_local);
events_lock->unlock();
printf("parsed %d into %d events with offset %d\n", dled, events->size(), event_offset);
}
void LogReader::readyRead() {
QByteArray dat = reply->readAll();
bStream.next_in = dat.data();
bStream.avail_in = dat.size();
while (bStream.avail_in > 0) {
int ret = BZ2_bzDecompress(&bStream);
if (ret != BZ_OK && ret != BZ_STREAM_END) {
qWarning() << "bz2 decompress failed";
break;
}
qDebug() << "got" << dat.size() << "with" << bStream.avail_out << "size" << raw.size();
}
int dled = raw.size() - bStream.avail_out;
cdled.put(dled);
}
-71
View File
@@ -1,71 +0,0 @@
#pragma once
#include <QString>
#include <QNetworkAccessManager>
#include <QWidget>
#include <QVector>
#include <QMultiMap>
#include <QElapsedTimer>
#include <QReadWriteLock>
#include <bzlib.h>
#include <kj/io.h>
#include <capnp/serialize.h>
#include "cereal/gen/cpp/log.capnp.h"
#include <thread>
#include "channel.hpp"
class FileReader : public QObject {
Q_OBJECT
public:
FileReader(const QString& file_);
void startRequest(const QUrl &url);
~FileReader();
virtual void readyRead();
void httpFinished();
virtual void done() {};
public slots:
void process();
protected:
QNetworkReply *reply;
private:
QNetworkAccessManager *qnam;
QElapsedTimer timer;
QString file;
};
typedef QMultiMap<uint64_t, cereal::Event::Reader> Events;
class LogReader : public FileReader {
Q_OBJECT
public:
LogReader(const QString& file, Events *, QReadWriteLock* events_lock_, QMap<int, QPair<int, int> > *eidx_);
~LogReader();
void readyRead();
void done() { is_done = true; };
bool is_done = false;
private:
bz_stream bStream;
// backing store
QByteArray raw;
std::thread *parser;
int event_offset;
channel<int> cdled;
// global
void mergeEvents(int dled);
Events *events;
QReadWriteLock* events_lock;
QMap<int, QPair<int, int> > *eidx;
};
-11
View File
@@ -1,11 +0,0 @@
Import('qt_env', 'messaging')
qt_env['CPPPATH'] += ["#tools/clib"]
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
libs = [messaging, 'avutil', 'avcodec', 'avformat', 'bz2', 'capnp', 'kj',
'pthread', 'swscale', 'zmq']
qt_env.Program("_nui",
['main.cpp', 'Unlogger.cpp', 'FileReader.cpp', '../clib/FrameReader.cpp'],
LIBS=qt_env['LIBS'] + libs)
-185
View File
@@ -1,185 +0,0 @@
#include <string>
#include <vector>
#include <capnp/dynamic.h>
#include <capnp/schema.h>
// include the dynamic struct
#include "cereal/gen/cpp/log.capnp.c++"
#include "cereal/gen/cpp/car.capnp.c++"
#include "cereal/gen/cpp/legacy.capnp.c++"
#include "cereal/services.h"
#include "Unlogger.hpp"
#include <stdint.h>
#include <time.h>
static inline uint64_t nanos_since_boot() {
struct timespec t;
#ifdef __APPLE__
clock_gettime(CLOCK_REALTIME, &t);
#else
clock_gettime(CLOCK_BOOTTIME, &t);
#endif
return t.tv_sec * 1000000000ULL + t.tv_nsec;
}
Unlogger::Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, FrameReader*> *frs_, int seek)
: events(events_), events_lock(events_lock_), frs(frs_) {
ctx = Context::create();
seek_request = seek*1e9;
QStringList block = QString(getenv("BLOCK")).split(",");
qDebug() << "blocklist" << block;
QStringList allow = QString(getenv("ALLOW")).split(",");
qDebug() << "allowlist" << allow;
for (const auto& it : services) {
std::string name = it.name;
if (allow[0].size() > 0 && !allow.contains(name.c_str())) {
qDebug() << "not allowing" << name.c_str();
continue;
}
if (block.contains(name.c_str())) {
qDebug() << "blocking" << name.c_str();
continue;
}
PubSocket *sock = PubSocket::create(ctx, name);
if (sock == NULL) {
qDebug() << "FAILED" << name.c_str();
continue;
}
qDebug() << name.c_str();
for (auto field: capnp::Schema::from<cereal::Event>().getFields()) {
std::string tname = field.getProto().getName();
if (tname == name) {
// TODO: I couldn't figure out how to get the which, only the index, hence this hack
int type = field.getIndex();
if (type > 67) type--; // valid
type--; // logMonoTime
//qDebug() << "here" << tname.c_str() << type << cereal::Event::CONTROLS_STATE;
socks.insert(type, sock);
}
}
}
}
void Unlogger::process() {
qDebug() << "hello from unlogger thread";
while (events->size() == 0) {
qDebug() << "waiting for events";
QThread::sleep(1);
}
qDebug() << "got events";
// TODO: hack
if (seek_request != 0) {
seek_request += events->begin().key();
while (events->lowerBound(seek_request) == events->end()) {
qDebug() << "waiting for desired time";
QThread::sleep(1);
}
}
QElapsedTimer timer;
timer.start();
uint64_t last_elapsed = 0;
// loops
while (1) {
uint64_t t0 = (events->begin()+1).key();
uint64_t t0r = timer.nsecsElapsed();
qDebug() << "unlogging at" << t0;
auto eit = events->lowerBound(t0);
while (eit != events->end()) {
while (paused) {
QThread::usleep(1000);
t0 = eit->getLogMonoTime();
t0r = timer.nsecsElapsed();
}
if (seek_request != 0) {
t0 = seek_request;
qDebug() << "seeking to" << t0;
t0r = timer.nsecsElapsed();
eit = events->lowerBound(t0);
seek_request = 0;
if (eit == events->end()) {
qWarning() << "seek off end";
break;
}
}
if (abs(((long long)tc-(long long)last_elapsed)) > 50e6) {
//qDebug() << "elapsed";
emit elapsed();
last_elapsed = tc;
}
auto e = *eit;
auto type = e.which();
uint64_t tm = e.getLogMonoTime();
auto it = socks.find(type);
tc = tm;
if (it != socks.end()) {
long etime = tm-t0;
long rtime = timer.nsecsElapsed() - t0r;
long us_behind = ((etime-rtime)*1e-3)+0.5;
if (us_behind > 0) {
if (us_behind > 1e6) {
qWarning() << "OVER ONE SECOND BEHIND, HACKING" << us_behind;
us_behind = 0;
t0 = tm;
t0r = timer.nsecsElapsed();
}
QThread::usleep(us_behind);
//qDebug() << "sleeping" << us_behind << etime << timer.nsecsElapsed();
}
capnp::MallocMessageBuilder msg;
msg.setRoot(e);
auto ee = msg.getRoot<cereal::Event>();
ee.setLogMonoTime(nanos_since_boot());
if (e.which() == cereal::Event::ROAD_CAMERA_STATE) {
auto fr = msg.getRoot<cereal::Event>().getRoadCameraState();
// TODO: better way?
auto it = eidx.find(fr.getFrameId());
if (it != eidx.end()) {
auto pp = *it;
//qDebug() << fr.getRoadCameraStateId() << pp;
if (frs->find(pp.first) != frs->end()) {
auto frm = (*frs)[pp.first];
auto data = frm->get(pp.second);
if (data != NULL) {
fr.setImage(kj::arrayPtr(data, frm->getRGBSize()));
}
}
}
}
auto words = capnp::messageToFlatArray(msg);
auto bytes = words.asBytes();
// TODO: Can PubSocket take a const char?
(*it)->send((char*)bytes.begin(), bytes.size());
}
++eit;
}
}
}
-33
View File
@@ -1,33 +0,0 @@
#pragma once
#include <QThread>
#include <QReadWriteLock>
#include "messaging.hpp"
#include "FileReader.hpp"
#include "FrameReader.hpp"
class Unlogger : public QObject {
Q_OBJECT
public:
Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, FrameReader*> *frs_, int seek);
uint64_t getCurrentTime() { return tc; }
void setSeekRequest(uint64_t seek_request_) { seek_request = seek_request_; }
void setPause(bool pause) { paused = pause; }
void togglePause() { paused = !paused; }
QMap<int, QPair<int, int> > eidx;
public slots:
void process();
signals:
void elapsed();
void finished();
private:
Events *events;
QReadWriteLock *events_lock;
QMap<int, FrameReader*> *frs;
QMap<int, PubSocket*> socks;
Context *ctx;
uint64_t tc = 0;
uint64_t seek_request = 0;
bool paused = false;
};
-14
View File
@@ -1,14 +0,0 @@
#!/usr/bin/env python3
import json
import sys
from tools.lib.route import Route
route_name = sys.argv[1]
routes = Route(route_name)
data_dump = {
"camera": routes.camera_paths(),
"logs": routes.log_paths()
}
json.dump(data_dump, open("routes.json", "w"))
-262
View File
@@ -1,262 +0,0 @@
#include <QApplication>
#include <QWidget>
#include <QString>
#include <QTimer>
#include <QPushButton>
#include <QGraphicsScene>
#include <QPainter>
#include <QThread>
#include <QMouseEvent>
#include <QReadWriteLock>
#include <QLineEdit>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#include <stdlib.h>
#include <QTextStream>
#include "FileReader.hpp"
#include "Unlogger.hpp"
#include "FrameReader.hpp"
class Window : public QWidget {
public:
Window(QString route_, int seek, int use_api);
bool addSegment(int i);
QJsonArray camera_paths;
QJsonArray log_paths;
int use_api;
protected:
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;
uint64_t ct;
Unlogger *unlogger;
private:
int timeToPixel(uint64_t ns);
uint64_t pixelToTime(int px);
QString route;
QReadWriteLock events_lock;
Events events;
int last_event_size = 0;
QMap<int, LogReader*> lrs;
QMap<int, FrameReader*> frs;
// cache the bar
QPixmap *px = NULL;
int seg_add = 0;
QLineEdit *timeLE;
};
Window::Window(QString route_, int seek, int use_api_) : route(route_), use_api(use_api_) {
timeLE = new QLineEdit(this);
timeLE->setPlaceholderText("Placeholder Text");
timeLE->move(50, 650);
QThread* thread = new QThread;
unlogger = new Unlogger(&events, &events_lock, &frs, seek);
unlogger->moveToThread(thread);
connect(thread, SIGNAL (started()), unlogger, SLOT (process()));
connect(unlogger, SIGNAL (elapsed()), this, SLOT (update()));
thread->start();
if (use_api) {
QString settings;
QFile file;
file.setFileName("routes.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
settings = file.readAll();
file.close();
QJsonDocument sd = QJsonDocument::fromJson(settings.toUtf8());
qWarning() << sd.isNull(); // <- print false :)
QJsonObject sett2 = sd.object();
this->camera_paths = sett2.value("camera").toArray();
this->log_paths = sett2.value("logs").toArray();
}
this->setFocusPolicy(Qt::StrongFocus);
// add the first segment
addSegment(seek/60);
}
bool Window::addSegment(int i) {
if (lrs.find(i) == lrs.end()) {
QString fn = QString("http://data.comma.life/%1/%2/rlog.bz2").arg(route).arg(i);
QThread* thread = new QThread;
if (!use_api) {
lrs.insert(i, new LogReader(fn, &events, &events_lock, &unlogger->eidx));
} else {
QString log_fn = this->log_paths.at(i).toString();
lrs.insert(i, new LogReader(log_fn, &events, &events_lock, &unlogger->eidx));
}
lrs[i]->moveToThread(thread);
connect(thread, SIGNAL (started()), lrs[i], SLOT (process()));
thread->start();
//connect(lrs[i], SIGNAL (finished()), this, SLOT (update()));
QString frn = QString("http://data.comma.life/%1/%2/fcamera.hevc").arg(route).arg(i);
if (!use_api) {
frs.insert(i, new FrameReader(qPrintable(frn)));
} else {
QString camera_fn = this->camera_paths.at(i).toString();
frs.insert(i, new FrameReader(qPrintable(camera_fn)));
}
return true;
}
return false;
}
#define PIXELS_PER_SEC 0.5
int Window::timeToPixel(uint64_t ns) {
// TODO: make this dynamic
return int(ns*1e-9*PIXELS_PER_SEC+0.5);
}
uint64_t Window::pixelToTime(int px) {
// TODO: make this dynamic
//printf("%d\n", px);
return ((px+0.5)/PIXELS_PER_SEC) * 1e9;
}
void Window::keyPressEvent(QKeyEvent *event) {
printf("keypress: %x\n", event->key());
if (event->key() == Qt::Key_Space) unlogger->togglePause();
}
void Window::mousePressEvent(QMouseEvent *event) {
//printf("mouse event\n");
if (event->button() == Qt::LeftButton) {
uint64_t t0 = events.begin().key();
uint64_t tt = pixelToTime(event->x());
int seg = int((tt*1e-9)/60);
printf("segment %d\n", seg);
addSegment(seg);
//printf("seek to %lu\n", t0+tt);
unlogger->setSeekRequest(t0+tt);
}
this->update();
}
void Window::paintEvent(QPaintEvent *event) {
if (events.size() == 0) return;
QElapsedTimer timer;
timer.start();
uint64_t t0 = events.begin().key();
//p.drawRect(0, 0, 600, 100);
// TODO: we really don't have to redraw this every time, only on updates to events
float vEgo = 0.;
int this_event_size = events.size();
if (last_event_size != this_event_size) {
if (px != NULL) delete px;
px = new QPixmap(1920, 600);
px->fill(QColor(0xd8, 0xd8, 0xd8));
QPainter tt(px);
tt.setBrush(Qt::cyan);
int lt = -1;
int lvv = 0;
for (auto e : events) {
auto type = e.which();
//printf("%lld %d\n", e.getLogMonoTime()-t0, type);
if (type == cereal::Event::CAR_STATE) {
vEgo = e.getCarState().getVEgo();
} else if (type == cereal::Event::CONTROLS_STATE) {
auto controlsState = e.getControlsState();
uint64_t t = (e.getLogMonoTime()-t0);
int enabled = controlsState.getState() == cereal::ControlsState::OpenpilotState::ENABLED;
int rt = timeToPixel(t); // 250 ms per pixel
if (rt != lt) {
int vv = vEgo*8.0;
if (lt != -1) {
tt.setPen(Qt::red);
tt.drawLine(lt, 300-lvv, rt, 300-vv);
if (enabled) {
tt.setPen(Qt::green);
} else {
tt.setPen(Qt::blue);
}
tt.drawLine(rt, 300, rt, 600);
}
lt = rt;
lvv = vv;
}
}
}
tt.end();
last_event_size = this_event_size;
if (lrs.find(seg_add) != lrs.end() && lrs[seg_add]->is_done) {
while (!addSegment(++seg_add));
}
}
QPainter p(this);
if (px != NULL) p.drawPixmap(0, 0, 1920, 600, *px);
p.setBrush(Qt::cyan);
uint64_t ct = unlogger->getCurrentTime();
if (ct != 0) {
addSegment((((ct-t0)*1e-9)/60)+1);
int rrt = timeToPixel(ct-t0);
p.drawRect(rrt-1, 0, 2, 600);
timeLE->setText(QString("%1").arg((ct-t0)*1e-9, '8', 'f', 2));
}
p.end();
if (timer.elapsed() > 50) {
qDebug() << "paint in" << timer.elapsed() << "ms";
}
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QString route(argv[1]);
int use_api = QString::compare(QString("use_api"), route, Qt::CaseInsensitive) == 0;
int seek = QString(argv[2]).toInt();
printf("seek: %d\n", seek);
route = route.replace("|", "/");
if (route == "") {
printf("usage %s: <route>\n", argv[0]);
exit(0);
//route = "3a5d6ac1c23e5536/2019-10-29--10-06-58";
//route = "0006c839f32a6f99/2019-02-18--06-21-29";
//route = "02ec6bea180a4d36/2019-10-25--10-18-09";
}
Window window(route, seek, use_api);
window.resize(1920, 800);
window.setWindowTitle("nui unlogger");
window.show();
return app.exec();
}
-18
View File
@@ -1,18 +0,0 @@
#!/bin/bash -e
if [ $# -gt 0 ]; then
if [ "$INTERNAL" = 1 ]; then
./_nui "$1"
else
./get_files_comma_api.py $1
if [ -f ./_nui ]; then
./_nui use_api
elif [ -f _nui.app/Contents/MacOS/_nui ]; then
./_nui.app/Contents/MacOS/_nui use_api
else
echo "nui not found, please build it"
fi
fi
else
echo "Please Enter a Route"
fi
-1
View File
@@ -1 +0,0 @@
test
-14
View File
@@ -1,14 +0,0 @@
#include "../../clib/FrameReader.hpp"
#include "TestFrameReader.hpp"
void TestFrameReader::frameread() {
QElapsedTimer t;
t.start();
FrameReader fr("3a5d6ac1c23e5536/2019-10-29--10-06-58/2/fcamera.hevc");
fr.get(2);
//QThread::sleep(10);
qDebug() << t.nsecsElapsed()*1e-9 << "seconds";
}
QTEST_MAIN(TestFrameReader)
-8
View File
@@ -1,8 +0,0 @@
#include <QtTest/QtTest>
class TestFrameReader : public QObject {
Q_OBJECT
private slots:
void frameread();
};
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:445cf96b9a1dea87579a730bc0d3b0f7d32fe15eb0e677d406abf7cf49ab75e1
size 463