qt replay (#20602)
* 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>pull/20740/head
parent
0fe155b7c3
commit
19d962cdf3
|
@ -417,9 +417,6 @@ SConscript(['selfdrive/ui/SConscript'])
|
||||||
if arch != "Darwin":
|
if arch != "Darwin":
|
||||||
SConscript(['selfdrive/logcatd/SConscript'])
|
SConscript(['selfdrive/logcatd/SConscript'])
|
||||||
|
|
||||||
if real_arch == "x86_64":
|
|
||||||
SConscript(['tools/nui/SConscript'])
|
|
||||||
|
|
||||||
external_sconscript = GetOption('external_sconscript')
|
external_sconscript = GetOption('external_sconscript')
|
||||||
if external_sconscript:
|
if external_sconscript:
|
||||||
SConscript([external_sconscript])
|
SConscript([external_sconscript])
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
moc_*
|
moc_*
|
||||||
*.moc
|
*.moc
|
||||||
|
|
||||||
|
replay/replay
|
||||||
qt/text
|
qt/text
|
||||||
qt/spinner
|
qt/spinner
|
||||||
qt/setup/setup
|
qt/setup/setup
|
||||||
|
|
|
@ -14,7 +14,8 @@ if arch == "Darwin":
|
||||||
widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/sound.cc",
|
widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/sound.cc",
|
||||||
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
||||||
"qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc",
|
"qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc",
|
||||||
"qt/widgets/scrollview.cc", "#phonelibs/qrcode/QrCode.cc"]
|
"qt/widgets/scrollview.cc", "#phonelibs/qrcode/QrCode.cc", "qt/api.cc",
|
||||||
|
"qt/request_repeater.cc"]
|
||||||
if arch != 'aarch64':
|
if arch != 'aarch64':
|
||||||
widgets_src += ["qt/offroad/networking.cc", "qt/offroad/wifiManager.cc"]
|
widgets_src += ["qt/offroad/networking.cc", "qt/offroad/wifiManager.cc"]
|
||||||
|
|
||||||
|
@ -26,9 +27,10 @@ qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs)
|
||||||
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=base_libs)
|
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=base_libs)
|
||||||
|
|
||||||
# build main UI
|
# build main UI
|
||||||
qt_src = ["main.cc", "ui.cc", "paint.cc", "sidebar.cc", "#phonelibs/nanovg/nanovg.c",
|
qt_src = ["main.cc", "ui.cc", "paint.cc", "sidebar.cc",
|
||||||
"qt/window.cc", "qt/home.cc", "qt/api.cc", "qt/offroad/settings.cc",
|
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
|
||||||
"qt/offroad/onboarding.cc"]
|
"qt/offroad/onboarding.cc", "#phonelibs/nanovg/nanovg.c"]
|
||||||
|
|
||||||
qt_env.Program("_ui", qt_src, LIBS=qt_libs)
|
qt_env.Program("_ui", qt_src, LIBS=qt_libs)
|
||||||
|
|
||||||
# setup, factory resetter, and installer
|
# setup, factory resetter, and installer
|
||||||
|
@ -55,3 +57,15 @@ if arch != 'aarch64' and "BUILD_SETUP" in os.environ:
|
||||||
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
|
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
|
||||||
obj = qt_env.Object(f"qt/setup/installer_{name}.o", ["qt/setup/installer.cc"], CPPDEFINES=d)
|
obj = qt_env.Object(f"qt/setup/installer_{name}.o", ["qt/setup/installer.cc"], CPPDEFINES=d)
|
||||||
qt_env.Program(f"qt/setup/installer_{name}", obj, LIBS=qt_libs, CPPDEFINES=d)
|
qt_env.Program(f"qt/setup/installer_{name}", obj, LIBS=qt_libs, CPPDEFINES=d)
|
||||||
|
|
||||||
|
# build headless replay
|
||||||
|
if arch == 'x86_64':
|
||||||
|
qt_env['CPPPATH'] += ["#tools/clib"]
|
||||||
|
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
|
||||||
|
|
||||||
|
replay_lib_src = ["replay/replay.cc", "replay/Unlogger.cpp",
|
||||||
|
"replay/FileReader.cpp", "#tools/clib/FrameReader.cpp"]
|
||||||
|
|
||||||
|
replay_lib = qt_env.Library("qt_replay", replay_lib_src, LIBS=base_libs)
|
||||||
|
replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'swscale', 'bz2'] + qt_libs
|
||||||
|
qt_env.Program("replay/replay", ["replay/main.cc"], LIBS=replay_libs)
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
#include "api.hpp"
|
#include "api.hpp"
|
||||||
#include "home.hpp"
|
|
||||||
#include "common/params.h"
|
#include "common/params.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
|
@ -72,21 +71,18 @@ QString CommaApi::create_jwt(QVector<QPair<QString, QJsonValue>> payloads, int e
|
||||||
return jwt;
|
return jwt;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, const QString &cache_key, bool disableWithScreen)
|
|
||||||
: disableWithScreen(disableWithScreen), cache_key(cache_key), QObject(parent) {
|
HttpRequest::HttpRequest(QObject *parent, QString requestURL, const QString &cache_key) : cache_key(cache_key), QObject(parent) {
|
||||||
networkAccessManager = new QNetworkAccessManager(this);
|
networkAccessManager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
reply = NULL;
|
reply = NULL;
|
||||||
|
|
||||||
QTimer* timer = new QTimer(this);
|
|
||||||
QObject::connect(timer, &QTimer::timeout, [=](){sendRequest(requestURL);});
|
|
||||||
timer->start(period_seconds * 1000);
|
|
||||||
|
|
||||||
networkTimer = new QTimer(this);
|
networkTimer = new QTimer(this);
|
||||||
networkTimer->setSingleShot(true);
|
networkTimer->setSingleShot(true);
|
||||||
networkTimer->setInterval(20000);
|
networkTimer->setInterval(20000);
|
||||||
connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
|
connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
|
||||||
|
|
||||||
|
sendRequest(requestURL);
|
||||||
|
|
||||||
if (!cache_key.isEmpty()) {
|
if (!cache_key.isEmpty()) {
|
||||||
if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
|
if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
|
||||||
QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
|
QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
|
||||||
|
@ -94,12 +90,7 @@ RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestRepeater::sendRequest(QString requestURL){
|
void HttpRequest::sendRequest(QString requestURL){
|
||||||
if (GLWindow::ui_state.scene.started || !active || reply != NULL ||
|
|
||||||
(!GLWindow::ui_state.awake && disableWithScreen)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString token = CommaApi::create_jwt();
|
QString token = CommaApi::create_jwt();
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setUrl(QUrl(requestURL));
|
request.setUrl(QUrl(requestURL));
|
||||||
|
@ -118,15 +109,16 @@ void RequestRepeater::sendRequest(QString requestURL){
|
||||||
connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
|
connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestRepeater::requestTimeout(){
|
void HttpRequest::requestTimeout(){
|
||||||
reply->abort();
|
reply->abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function should always emit something
|
// This function should always emit something
|
||||||
void RequestRepeater::requestFinished(){
|
void HttpRequest::requestFinished(){
|
||||||
if (reply->error() != QNetworkReply::OperationCanceledError) {
|
if (reply->error() != QNetworkReply::OperationCanceledError) {
|
||||||
networkTimer->stop();
|
networkTimer->stop();
|
||||||
QString response = reply->readAll();
|
QString response = reply->readAll();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
// save to cache
|
// save to cache
|
||||||
if (!cache_key.isEmpty()) {
|
if (!cache_key.isEmpty()) {
|
||||||
|
|
|
@ -26,22 +26,21 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes repeated requests to the request endpoint.
|
* Makes a request to the request endpoint.
|
||||||
*/
|
*/
|
||||||
class RequestRepeater : public QObject {
|
|
||||||
|
class HttpRequest : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, const QString &cache_key = "", bool disableWithScreen = true);
|
explicit HttpRequest(QObject* parent, QString requestURL, const QString &cache_key = "");
|
||||||
bool active = true;
|
QNetworkReply *reply;
|
||||||
|
void sendRequest(QString requestURL);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool disableWithScreen;
|
QNetworkAccessManager *networkAccessManager;
|
||||||
QNetworkReply* reply;
|
QTimer *networkTimer;
|
||||||
QNetworkAccessManager* networkAccessManager;
|
|
||||||
QTimer* networkTimer;
|
|
||||||
QString cache_key;
|
QString cache_key;
|
||||||
void sendRequest(QString requestURL);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void requestTimeout();
|
void requestTimeout();
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "request_repeater.hpp"
|
||||||
|
|
||||||
|
RequestRepeater::RequestRepeater(QObject *parent, QString requestURL, const QString &cache_key, int period_seconds, bool disableWithScreen) :
|
||||||
|
HttpRequest(parent, requestURL, cache_key), disableWithScreen(disableWithScreen) {
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
QObject::connect(timer, &QTimer::timeout, [=](){
|
||||||
|
if (!GLWindow::ui_state.scene.started && reply == NULL && (GLWindow::ui_state.awake || !disableWithScreen)) {
|
||||||
|
sendRequest(requestURL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timer->start(period_seconds * 1000);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "api.hpp"
|
||||||
|
#include "home.hpp"
|
||||||
|
|
||||||
|
class RequestRepeater : public HttpRequest {
|
||||||
|
|
||||||
|
public:
|
||||||
|
RequestRepeater(QObject *parent, QString requestURL, const QString &cache_key = "", int period_seconds = 0, bool disableWithScreen = true);
|
||||||
|
bool disableWithScreen;
|
||||||
|
};
|
|
@ -3,9 +3,9 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "api.hpp"
|
|
||||||
#include "common/params.h"
|
#include "common/params.h"
|
||||||
#include "drive_stats.hpp"
|
#include "drive_stats.hpp"
|
||||||
|
#include "request_repeater.hpp"
|
||||||
|
|
||||||
const double MILE_TO_KM = 1.60934;
|
const double MILE_TO_KM = 1.60934;
|
||||||
|
|
||||||
|
@ -65,6 +65,6 @@ DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
|
||||||
// TODO: do we really need to update this frequently?
|
// TODO: do we really need to update this frequently?
|
||||||
QString dongleId = QString::fromStdString(Params().get("DongleId"));
|
QString dongleId = QString::fromStdString(Params().get("DongleId"));
|
||||||
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/stats";
|
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/stats";
|
||||||
RequestRepeater* repeater = new RequestRepeater(this, url, 13, "ApiCache_DriveStats");
|
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_DriveStats", 13);
|
||||||
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString)));
|
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "QrCode.hpp"
|
#include "QrCode.hpp"
|
||||||
#include "api.hpp"
|
#include "request_repeater.hpp"
|
||||||
#include "common/params.h"
|
#include "common/params.h"
|
||||||
#include "setup.hpp"
|
#include "setup.hpp"
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString url = "https://api.commadotai.com/v1/devices/" + dongleId + "/owner";
|
QString url = "https://api.commadotai.com/v1/devices/" + dongleId + "/owner";
|
||||||
RequestRepeater* repeater = new RequestRepeater(this, url, 6, "ApiCache_Owner");
|
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_Owner", 6);
|
||||||
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
|
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
|
||||||
// set up API requests
|
// set up API requests
|
||||||
QString dongleId = QString::fromStdString(Params().get("DongleId"));
|
QString dongleId = QString::fromStdString(Params().get("DongleId"));
|
||||||
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/";
|
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/";
|
||||||
RequestRepeater* repeater = new RequestRepeater(this, url, 5, "ApiCache_Device");
|
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
|
||||||
|
|
||||||
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
|
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
|
||||||
QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
|
QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
|
||||||
|
|
|
@ -14,16 +14,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static inline uint64_t nanos_since_boot() {
|
#include "common/timing.h"
|
||||||
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)
|
Unlogger::Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, FrameReader*> *frs_, int seek)
|
||||||
: events(events_), events_lock(events_lock_), frs(frs_) {
|
: events(events_), events_lock(events_lock_), frs(frs_) {
|
||||||
|
@ -57,23 +48,12 @@ Unlogger::Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, Fram
|
||||||
|
|
||||||
qDebug() << name.c_str();
|
qDebug() << name.c_str();
|
||||||
|
|
||||||
for (auto field: capnp::Schema::from<cereal::Event>().getFields()) {
|
socks.insert(name, sock);
|
||||||
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() {
|
void Unlogger::process(SubMaster *sm) {
|
||||||
|
|
||||||
qDebug() << "hello from unlogger thread";
|
qDebug() << "hello from unlogger thread";
|
||||||
while (events->size() == 0) {
|
while (events->size() == 0) {
|
||||||
qDebug() << "waiting for events";
|
qDebug() << "waiting for events";
|
||||||
|
@ -103,6 +83,12 @@ void Unlogger::process() {
|
||||||
|
|
||||||
auto eit = events->lowerBound(t0);
|
auto eit = events->lowerBound(t0);
|
||||||
while (eit != events->end()) {
|
while (eit != events->end()) {
|
||||||
|
|
||||||
|
float time_to_end = ((events->lastKey() - eit.key())/1e9);
|
||||||
|
if (loading_segment && (time_to_end > 20.0)){
|
||||||
|
loading_segment = false;
|
||||||
|
}
|
||||||
|
|
||||||
while (paused) {
|
while (paused) {
|
||||||
QThread::usleep(1000);
|
QThread::usleep(1000);
|
||||||
t0 = eit->getLogMonoTime();
|
t0 = eit->getLogMonoTime();
|
||||||
|
@ -127,8 +113,14 @@ void Unlogger::process() {
|
||||||
last_elapsed = tc;
|
last_elapsed = tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto e = *eit;
|
cereal::Event::Reader e = *eit;
|
||||||
auto type = e.which();
|
|
||||||
|
capnp::DynamicStruct::Reader e_ds = static_cast<capnp::DynamicStruct::Reader>(e);
|
||||||
|
std::string type;
|
||||||
|
KJ_IF_MAYBE(e_, e_ds.which()){
|
||||||
|
type = e_->getProto().getName();
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t tm = e.getLogMonoTime();
|
uint64_t tm = e.getLogMonoTime();
|
||||||
auto it = socks.find(type);
|
auto it = socks.find(type);
|
||||||
tc = tm;
|
tc = tm;
|
||||||
|
@ -147,39 +139,56 @@ void Unlogger::process() {
|
||||||
//qDebug() << "sleeping" << us_behind << etime << timer.nsecsElapsed();
|
//qDebug() << "sleeping" << us_behind << etime << timer.nsecsElapsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
capnp::MallocMessageBuilder msg;
|
if (type == "roadCameraState") {
|
||||||
msg.setRoot(e);
|
auto fr = e.getRoadCameraState();
|
||||||
|
|
||||||
auto ee = msg.getRoot<cereal::Event>();
|
auto it_ = eidx.find(fr.getFrameId());
|
||||||
ee.setLogMonoTime(nanos_since_boot());
|
if (it_ != eidx.end()) {
|
||||||
|
auto pp = *it_;
|
||||||
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;
|
//qDebug() << fr.getRoadCameraStateId() << pp;
|
||||||
|
|
||||||
if (frs->find(pp.first) != frs->end()) {
|
if (frs->find(pp.first) != frs->end()) {
|
||||||
auto frm = (*frs)[pp.first];
|
auto frm = (*frs)[pp.first];
|
||||||
auto data = frm->get(pp.second);
|
auto data = frm->get(pp.second);
|
||||||
if (data != NULL) {
|
|
||||||
fr.setImage(kj::arrayPtr(data, frm->getRGBSize()));
|
if (vipc_server == nullptr) {
|
||||||
|
cl_device_id device_id = cl_get_device_id(CL_DEVICE_TYPE_DEFAULT);
|
||||||
|
cl_context context = CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
|
||||||
|
|
||||||
|
vipc_server = new VisionIpcServer("camerad", device_id, context);
|
||||||
|
vipc_server->create_buffers(VisionStreamType::VISION_STREAM_RGB_BACK, 4, true, frm->width, frm->height);
|
||||||
|
|
||||||
|
vipc_server->start_listener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VisionBuf *buf = vipc_server->get_buffer(VisionStreamType::VISION_STREAM_RGB_BACK);
|
||||||
|
memcpy(buf->addr, data, frm->getRGBSize());
|
||||||
|
VisionIpcBufExtra extra = {};
|
||||||
|
|
||||||
|
vipc_server->send(buf, &extra, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto words = capnp::messageToFlatArray(msg);
|
if (sm == nullptr){
|
||||||
auto bytes = words.asBytes();
|
capnp::MallocMessageBuilder msg;
|
||||||
|
msg.setRoot(e);
|
||||||
|
auto words = capnp::messageToFlatArray(msg);
|
||||||
|
auto bytes = words.asBytes();
|
||||||
|
|
||||||
// TODO: Can PubSocket take a const char?
|
(*it)->send((char*)bytes.begin(), bytes.size());
|
||||||
(*it)->send((char*)bytes.begin(), bytes.size());
|
} else{
|
||||||
|
std::vector<std::pair<std::string, cereal::Event::Reader>> messages;
|
||||||
|
messages.push_back({type, e});
|
||||||
|
sm->update_msgs(nanos_since_boot(), messages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++eit;
|
++eit;
|
||||||
|
|
||||||
|
if (time_to_end < 10.0 && !loading_segment){
|
||||||
|
loading_segment = true;
|
||||||
|
emit loadSegment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
#include "clutil.h"
|
||||||
#include "messaging.hpp"
|
#include "messaging.hpp"
|
||||||
#include "FileReader.hpp"
|
#include "FileReader.hpp"
|
||||||
#include "FrameReader.hpp"
|
#include "FrameReader.hpp"
|
||||||
|
#include "visionipc_server.h"
|
||||||
|
|
||||||
class Unlogger : public QObject {
|
class Unlogger : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -15,19 +17,24 @@ Q_OBJECT
|
||||||
void setPause(bool pause) { paused = pause; }
|
void setPause(bool pause) { paused = pause; }
|
||||||
void togglePause() { paused = !paused; }
|
void togglePause() { paused = !paused; }
|
||||||
QMap<int, QPair<int, int> > eidx;
|
QMap<int, QPair<int, int> > eidx;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void process();
|
void process(SubMaster *sm = nullptr);
|
||||||
signals:
|
signals:
|
||||||
void elapsed();
|
void elapsed();
|
||||||
void finished();
|
void finished();
|
||||||
|
void loadSegment();
|
||||||
private:
|
private:
|
||||||
Events *events;
|
Events *events;
|
||||||
QReadWriteLock *events_lock;
|
QReadWriteLock *events_lock;
|
||||||
QMap<int, FrameReader*> *frs;
|
QMap<int, FrameReader*> *frs;
|
||||||
QMap<int, PubSocket*> socks;
|
QMap<std::string, PubSocket*> socks;
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
uint64_t tc = 0;
|
uint64_t tc = 0;
|
||||||
uint64_t seek_request = 0;
|
uint64_t seek_request = 0;
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
|
bool loading_segment = false;
|
||||||
|
|
||||||
|
VisionIpcServer *vipc_server = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include "replay.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]){
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QString route(argv[1]);
|
||||||
|
if (route == "") {
|
||||||
|
printf("Usage: ./replay \"route\"\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Replay *replay = new Replay(route, 0);
|
||||||
|
replay->stream(0);
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "replay.hpp"
|
||||||
|
|
||||||
|
Replay::Replay(QString route_, int seek) : route(route_) {
|
||||||
|
unlogger = new Unlogger(&events, &events_lock, &frs, seek);
|
||||||
|
current_segment = 0;
|
||||||
|
|
||||||
|
http = new HttpRequest(this, "https://api.commadotai.com/v1/route/" + route + "/files");
|
||||||
|
QObject::connect(http, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Replay::parseResponse(QString response){
|
||||||
|
response = response.trimmed();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
||||||
|
|
||||||
|
if (doc.isNull()) {
|
||||||
|
qDebug() << "JSON Parse failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_paths = doc["cameras"].toArray();
|
||||||
|
log_paths = doc["logs"].toArray();
|
||||||
|
|
||||||
|
// add first segment
|
||||||
|
addSegment(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Replay::addSegment(int i){
|
||||||
|
if (lrs.find(i) != lrs.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QThread* thread = new QThread;
|
||||||
|
|
||||||
|
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);
|
||||||
|
QObject::connect(thread, SIGNAL (started()), lrs[i], SLOT (process()));
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
QString camera_fn = this->camera_paths.at(i).toString();
|
||||||
|
frs.insert(i, new FrameReader(qPrintable(camera_fn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Replay::trimSegment(int n){
|
||||||
|
event_sizes.enqueue(events.size() - event_sizes.last());
|
||||||
|
auto first = events.begin();
|
||||||
|
|
||||||
|
for(int i = 0 ; i < n ; i++){
|
||||||
|
int remove = event_sizes.dequeue();
|
||||||
|
for(int j = 0 ; j < remove ; j++){
|
||||||
|
first = events.erase(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Replay::stream(SubMaster *sm){
|
||||||
|
QThread* thread = new QThread;
|
||||||
|
unlogger->moveToThread(thread);
|
||||||
|
QObject::connect(thread, &QThread::started, [=](){
|
||||||
|
unlogger->process(sm);
|
||||||
|
});
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
QObject::connect(unlogger, &Unlogger::loadSegment, [=](){
|
||||||
|
addSegment(++current_segment);
|
||||||
|
if (current_segment > 1) {
|
||||||
|
trimSegment(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include <capnp/dynamic.h>
|
||||||
|
|
||||||
|
#include "qt/api.hpp"
|
||||||
|
#include "Unlogger.hpp"
|
||||||
|
#include "FileReader.hpp"
|
||||||
|
#include "FrameReader.hpp"
|
||||||
|
#include "visionipc_server.h"
|
||||||
|
|
||||||
|
class Replay : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Replay(QString route_, int seek);
|
||||||
|
void stream(SubMaster *sm = nullptr);
|
||||||
|
void addSegment(int i);
|
||||||
|
void trimSegment(int n);
|
||||||
|
QJsonArray camera_paths;
|
||||||
|
QJsonArray log_paths;
|
||||||
|
|
||||||
|
QQueue<int> event_sizes;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void parseResponse(QString response);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Unlogger *unlogger;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString route;
|
||||||
|
|
||||||
|
QReadWriteLock events_lock;
|
||||||
|
Events events;
|
||||||
|
|
||||||
|
QMap<int, LogReader*> lrs;
|
||||||
|
QMap<int, FrameReader*> frs;
|
||||||
|
HttpRequest *http;
|
||||||
|
|
||||||
|
int current_segment;
|
||||||
|
};
|
||||||
|
|
|
@ -30,15 +30,17 @@ public:
|
||||||
int getRGBSize() { return width*height*3; }
|
int getRGBSize() { return width*height*3; }
|
||||||
void loaderThread();
|
void loaderThread();
|
||||||
void cacherThread();
|
void cacherThread();
|
||||||
|
|
||||||
|
//TODO: get this from the actual frame
|
||||||
|
int width = 1164;
|
||||||
|
int height = 874;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AVFormatContext *pFormatCtx = NULL;
|
AVFormatContext *pFormatCtx = NULL;
|
||||||
AVCodecContext *pCodecCtx = NULL;
|
AVCodecContext *pCodecCtx = NULL;
|
||||||
|
|
||||||
struct SwsContext *sws_ctx = NULL;
|
struct SwsContext *sws_ctx = NULL;
|
||||||
|
|
||||||
int width = 1164;
|
|
||||||
int height = 874;
|
|
||||||
|
|
||||||
std::vector<AVPacket *> pkts;
|
std::vector<AVPacket *> pkts;
|
||||||
|
|
||||||
std::thread *t;
|
std::thread *t;
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
.*.swp
|
|
||||||
*.o
|
|
||||||
_nui
|
|
||||||
moc_*
|
|
||||||
nui.app/*
|
|
||||||
routes.json
|
|
||||||
_nui.app
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -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"))
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 +0,0 @@
|
||||||
test
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include <QtTest/QtTest>
|
|
||||||
|
|
||||||
class TestFrameReader : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
private slots:
|
|
||||||
void frameread();
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
######################################################################
|
|
||||||
# Automatically generated by qmake (3.0) Thu Oct 31 16:05:48 2019
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
QT += testlib
|
|
||||||
TEMPLATE = app
|
|
||||||
TARGET = test
|
|
||||||
INCLUDEPATH += . ../
|
|
||||||
|
|
||||||
# Input
|
|
||||||
SOURCES += TestFrameReader.cpp ../../clib/FrameReader.cpp
|
|
||||||
HEADERS = TestFrameReader.hpp ../../clib/FrameReader.hpp
|
|
||||||
|
|
||||||
CONFIG += c++14
|
|
||||||
|
|
||||||
LIBS += -lavformat -lavcodec -lavutil -lswscale
|
|
Loading…
Reference in New Issue