Adding //aos/vision/debug:debug_framework.
This will allow easy construction of year-specific debug viewers.
Currently supported source types are:
- blobs over tcp
- jpegs from a camera
- blobs from a log
- a random list of jpegs
Change-Id: I1d73f82f98ca5f60b0135ea0dd588759056e0c40
diff --git a/aos/vision/debug/blob_log-source.cc b/aos/vision/debug/blob_log-source.cc
new file mode 100644
index 0000000..f707342
--- /dev/null
+++ b/aos/vision/debug/blob_log-source.cc
@@ -0,0 +1,184 @@
+#include "aos/vision/debug/debug_framework.h"
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fstream>
+#include <functional>
+#include <string>
+
+#include "aos/vision/blob/codec.h"
+
+namespace aos {
+namespace vision {
+
+namespace {
+
+long GetFileSize(const std::string& filename) {
+ struct stat stat_buf;
+ int rc = stat(filename.c_str(), &stat_buf);
+ return rc == 0 ? stat_buf.st_size : -1;
+}
+
+// Parses the blob-log file format.
+// File format goes:
+//
+// Repeated:
+//
+// frame_length.
+// timestamp.
+// fmt.w
+// fmt.h
+// Encoded blob.
+class InputFile {
+ public:
+ InputFile(const std::string &fname)
+ : ifs_(fname, std::ifstream::in), len_(GetFileSize(fname)) {
+ if (len_ <= 0) {
+ LOG(FATAL, "File (%s) not found. Size (%d)\n", fname.c_str(), (int)len_);
+ }
+ // assert(len_ > 0);
+ tmp_buf_.resize(len_, 0);
+ ifs_.read(&tmp_buf_[0], len_);
+ buf_ = &tmp_buf_[0];
+ }
+
+ bool ReadNext(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
+ if (buf_ - &tmp_buf_[0] >= len_) return false;
+ if (prev_ != nullptr) prev_frames_.emplace_back(prev_);
+ prev_ = buf_;
+ DoRead(blob_list, fmt, timestamp);
+ return true;
+ }
+
+ bool ReadPrev(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
+ if (prev_frames_.empty()) return false;
+ buf_ = prev_frames_.back();
+ prev_frames_.pop_back();
+ buf_ += sizeof(uint32_t);
+ DoRead(blob_list, fmt, timestamp);
+ prev_ = nullptr;
+ return true;
+ }
+
+ private:
+ void DoRead(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
+ buf_ += sizeof(uint32_t);
+ *timestamp = Int64Codec::Read(&buf_);
+ fmt->w = Int32Codec::Read(&buf_);
+ fmt->h = Int32Codec::Read(&buf_);
+ buf_ = ParseBlobList(blob_list, buf_);
+ }
+ std::vector<const char *> prev_frames_;
+ const char *buf_;
+ const char *prev_ = nullptr;
+ std::ifstream ifs_;
+
+ long len_;
+ std::vector<char> tmp_buf_;
+};
+
+// A single parsed frame.
+class BlobStreamFrame {
+ public:
+ BlobList blob_list;
+ ImageFormat fmt;
+ uint64_t timestamp;
+ void ReadNext(InputFile *fin) {
+ blob_list.clear();
+ if (!fin->ReadNext(&blob_list, &fmt, ×tamp)) {
+ exit(0);
+ return;
+ }
+ }
+ bool ReadPrev(InputFile *fin) {
+ blob_list.clear();
+ return fin->ReadPrev(&blob_list, &fmt, ×tamp);
+ }
+};
+
+} // namespace
+
+// class for installing a lambda as a gtk timeout.
+class TimeoutCallback {
+ public:
+ TimeoutCallback() {}
+
+ void Reset(guint32 interval, std::function<bool()> callback) {
+ Stop();
+ callback_ = callback;
+ timeout_key_ = g_timeout_add(interval, &TimeoutCallback::Callback, this);
+ }
+ void Stop() {
+ if (callback_) {
+ g_source_remove(timeout_key_);
+ }
+ callback_ = std::function<bool()>();
+ }
+
+ private:
+ static gint Callback(void *self) {
+ return reinterpret_cast<TimeoutCallback *>(self)->Callback();
+ }
+ gint Callback() {
+ auto callback = callback_;
+ if (!callback()) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+ gint timeout_key_;
+ std::function<bool()> callback_;
+};
+
+class BlobLogImageSource : public ImageSource {
+ public:
+ void Init(const std::string &blob_log_filename,
+ DebugFrameworkInterface *interface) override {
+ interface_ = interface;
+ image_source_.reset(new InputFile(blob_log_filename));
+
+ // Tick 25 fps.
+ // TODO(parker): Make this FPS configurable.
+ cb_.Reset(1000 / 25, [this]() { return Tick(); });
+
+ frame_.ReadNext(image_source_.get());
+ interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ interface_->InstallKeyPress([this](uint32_t keyval) {
+ if (keyval == GDK_KEY_Left) {
+ frame_.ReadPrev(image_source_.get());
+ interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ } else if (keyval == GDK_KEY_Right) {
+ frame_.ReadNext(image_source_.get());
+ interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ } else {
+ return;
+ }
+ });
+ }
+
+ bool Tick() {
+ frame_.ReadNext(image_source_.get());
+ interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ return true;
+ }
+
+ const char *GetHelpMessage() override {
+ return &R"(
+ format_spec is the name of a file in blob list format.
+ This viewer source will stream blobs from the log.
+)"[1];
+ }
+
+ private:
+ TimeoutCallback cb_;
+ DebugFrameworkInterface *interface_ = nullptr;
+ std::unique_ptr<InputFile> image_source_;
+ BlobStreamFrame frame_;
+};
+
+REGISTER_IMAGE_SOURCE("blob_log", BlobLogImageSource);
+
+} // namespace vision
+} // namespace aos