blob: f2bfbb517399b21b8fdd80c51677fafa38fc808e [file] [log] [blame]
Parker Schuh90641112017-02-25 12:18:36 -08001#include "aos/vision/debug/debug_framework.h"
2
3#include <gdk/gdk.h>
4#include <gtk/gtk.h>
5#include <sys/stat.h>
6#include <unistd.h>
7#include <fstream>
8#include <functional>
9#include <string>
10
11#include "aos/vision/blob/codec.h"
12
13namespace aos {
14namespace vision {
15
16namespace {
17
Parker Schuhef47dbf2017-03-04 16:59:30 -080018long GetFileSize(const std::string &filename) {
Parker Schuh90641112017-02-25 12:18:36 -080019 struct stat stat_buf;
20 int rc = stat(filename.c_str(), &stat_buf);
21 return rc == 0 ? stat_buf.st_size : -1;
22}
23
24// Parses the blob-log file format.
25// File format goes:
26//
27// Repeated:
28//
29// frame_length.
30// timestamp.
31// fmt.w
32// fmt.h
33// Encoded blob.
34class InputFile {
35 public:
36 InputFile(const std::string &fname)
37 : ifs_(fname, std::ifstream::in), len_(GetFileSize(fname)) {
38 if (len_ <= 0) {
39 LOG(FATAL, "File (%s) not found. Size (%d)\n", fname.c_str(), (int)len_);
40 }
41 // assert(len_ > 0);
42 tmp_buf_.resize(len_, 0);
43 ifs_.read(&tmp_buf_[0], len_);
44 buf_ = &tmp_buf_[0];
45 }
46
47 bool ReadNext(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
48 if (buf_ - &tmp_buf_[0] >= len_) return false;
49 if (prev_ != nullptr) prev_frames_.emplace_back(prev_);
50 prev_ = buf_;
51 DoRead(blob_list, fmt, timestamp);
52 return true;
53 }
54
55 bool ReadPrev(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
56 if (prev_frames_.empty()) return false;
57 buf_ = prev_frames_.back();
58 prev_frames_.pop_back();
59 buf_ += sizeof(uint32_t);
60 DoRead(blob_list, fmt, timestamp);
61 prev_ = nullptr;
62 return true;
63 }
64
65 private:
66 void DoRead(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
67 buf_ += sizeof(uint32_t);
68 *timestamp = Int64Codec::Read(&buf_);
69 fmt->w = Int32Codec::Read(&buf_);
70 fmt->h = Int32Codec::Read(&buf_);
71 buf_ = ParseBlobList(blob_list, buf_);
72 }
73 std::vector<const char *> prev_frames_;
74 const char *buf_;
75 const char *prev_ = nullptr;
76 std::ifstream ifs_;
77
78 long len_;
79 std::vector<char> tmp_buf_;
80};
81
82// A single parsed frame.
83class BlobStreamFrame {
84 public:
85 BlobList blob_list;
86 ImageFormat fmt;
87 uint64_t timestamp;
88 void ReadNext(InputFile *fin) {
89 blob_list.clear();
90 if (!fin->ReadNext(&blob_list, &fmt, &timestamp)) {
91 exit(0);
92 return;
93 }
94 }
95 bool ReadPrev(InputFile *fin) {
96 blob_list.clear();
97 return fin->ReadPrev(&blob_list, &fmt, &timestamp);
98 }
99};
100
101} // namespace
102
103// class for installing a lambda as a gtk timeout.
104class TimeoutCallback {
105 public:
106 TimeoutCallback() {}
107
108 void Reset(guint32 interval, std::function<bool()> callback) {
109 Stop();
110 callback_ = callback;
111 timeout_key_ = g_timeout_add(interval, &TimeoutCallback::Callback, this);
112 }
113 void Stop() {
114 if (callback_) {
115 g_source_remove(timeout_key_);
116 }
117 callback_ = std::function<bool()>();
118 }
119
120 private:
121 static gint Callback(void *self) {
122 return reinterpret_cast<TimeoutCallback *>(self)->Callback();
123 }
124 gint Callback() {
125 auto callback = callback_;
126 if (!callback()) {
127 return FALSE;
128 }
129 return TRUE;
130 }
131 gint timeout_key_;
132 std::function<bool()> callback_;
133};
134
135class BlobLogImageSource : public ImageSource {
136 public:
137 void Init(const std::string &blob_log_filename,
138 DebugFrameworkInterface *interface) override {
139 interface_ = interface;
140 image_source_.reset(new InputFile(blob_log_filename));
141
142 // Tick 25 fps.
143 // TODO(parker): Make this FPS configurable.
144 cb_.Reset(1000 / 25, [this]() { return Tick(); });
145
146 frame_.ReadNext(image_source_.get());
147 interface_->NewBlobList(frame_.blob_list, frame_.fmt);
148 interface_->InstallKeyPress([this](uint32_t keyval) {
149 if (keyval == GDK_KEY_Left) {
150 frame_.ReadPrev(image_source_.get());
151 interface_->NewBlobList(frame_.blob_list, frame_.fmt);
152 } else if (keyval == GDK_KEY_Right) {
153 frame_.ReadNext(image_source_.get());
154 interface_->NewBlobList(frame_.blob_list, frame_.fmt);
155 } else {
156 return;
157 }
158 });
159 }
160
161 bool Tick() {
162 frame_.ReadNext(image_source_.get());
163 interface_->NewBlobList(frame_.blob_list, frame_.fmt);
164 return true;
165 }
166
167 const char *GetHelpMessage() override {
168 return &R"(
169 format_spec is the name of a file in blob list format.
170 This viewer source will stream blobs from the log.
171)"[1];
172 }
173
174 private:
175 TimeoutCallback cb_;
176 DebugFrameworkInterface *interface_ = nullptr;
177 std::unique_ptr<InputFile> image_source_;
178 BlobStreamFrame frame_;
179};
180
181REGISTER_IMAGE_SOURCE("blob_log", BlobLogImageSource);
182
183} // namespace vision
184} // namespace aos