Checking in debug_view, some extra missing utils, and the y2016 target_sender code.

Change-Id: I241947265da8f332c39862f4d0ddcdc2d29c7b68
diff --git a/aos/common/scoped_fd.h b/aos/common/scoped_fd.h
index 57ecdd8..29ccf6e 100644
--- a/aos/common/scoped_fd.h
+++ b/aos/common/scoped_fd.h
@@ -1,7 +1,10 @@
+#ifndef _AOS_COMMON_SCOPED_FD_
+#define _AOS_COMMON_SCOPED_FD_
+
 #include <unistd.h>
 
-#include "aos/common/macros.h"
 #include "aos/common/logging/logging.h"
+#include "aos/common/macros.h"
 
 namespace aos {
 
@@ -9,9 +12,7 @@
 class ScopedFD {
  public:
   explicit ScopedFD(int fd = -1) : fd_(fd) {}
-  ~ScopedFD() {
-    Close();
-  }
+  ~ScopedFD() { Close(); }
   int get() const { return fd_; }
   int release() {
     const int r = fd_;
@@ -25,6 +26,7 @@
     }
   }
   operator bool() const { return fd_ != -1; }
+
  private:
   int fd_;
   void Close() {
@@ -38,3 +40,5 @@
 };
 
 }  // namespace aos
+
+#endif  // _AOS_COMMON_SCOPED_FD_
diff --git a/aos/vision/blob/BUILD b/aos/vision/blob/BUILD
index a024aba..8cb4373 100644
--- a/aos/vision/blob/BUILD
+++ b/aos/vision/blob/BUILD
@@ -93,7 +93,6 @@
   ],
 )
 
-"""
 cc_library(
   name = 'stream_view',
   hdrs = ['stream_view.h'],
@@ -103,4 +102,3 @@
     '//aos/vision/image:image_types',
   ],
 )
-"""
diff --git a/aos/vision/debug/BUILD b/aos/vision/debug/BUILD
new file mode 100644
index 0000000..91111b2
--- /dev/null
+++ b/aos/vision/debug/BUILD
@@ -0,0 +1,22 @@
+#!/usr/bin/python3
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+    name = "overlay",
+    hdrs = ["overlay.h"],
+    deps = [
+        '//aos/vision/math:vector',
+        '//aos/vision/math:segment',
+        '//aos/vision/image:image_types',
+        ],
+)
+
+cc_library(name = "debug_viewer",
+    srcs = ["debug_viewer.cc"],
+    hdrs = ["debug_viewer.h"],
+    deps = [
+        '@usr_repo//:gtk+-3.0',
+        "//aos/vision/image:image_types",
+        ":overlay",
+    ]
+)
diff --git a/aos/vision/debug/debug_viewer.cc b/aos/vision/debug/debug_viewer.cc
new file mode 100644
index 0000000..331f733
--- /dev/null
+++ b/aos/vision/debug/debug_viewer.cc
@@ -0,0 +1,166 @@
+#include "aos/vision/debug/debug_viewer.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "aos/vision/image/image_types.h"
+
+namespace aos {
+namespace vision {
+
+template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
+gboolean DrawCallback(GtkWidget *, cairo_t *cr, gpointer data) {
+  return ((*((T *)data)).*DrawMethod)(cr);
+}
+
+template <typename T, gboolean (T::*DrawMethod)(cairo_t *cr)>
+void g_draw_signal_connect(GtkWidget *widget, T *obj) {
+  gboolean (*fnptr)(GtkWidget *, cairo_t *, gpointer) =
+      DrawCallback<T, DrawMethod>;
+  g_signal_connect(widget, "draw", G_CALLBACK(fnptr), obj);
+}
+
+struct DebugViewer::Internals {
+  Internals(bool flip) : flip_(flip) {}
+
+  gboolean Draw(cairo_t *cr) {
+    needs_draw = false;
+    cairo_scale(cr, scale_factor, scale_factor);
+    if (pixbuf != nullptr) {
+      cairo_save(cr);
+      if (flip_) {
+        cairo_translate(cr, ptr.fmt().w, ptr.fmt().h);
+        cairo_scale(cr, -1, -1);
+      }
+      gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
+      cairo_paint(cr);
+      cairo_restore(cr);
+    }
+
+    int w = ptr.fmt().w;
+    int h = ptr.fmt().h;
+    if (overlays) {
+      for (const auto &ov : *overlays) {
+        cairo_save(cr);
+        CairoRender render(cr);
+        // move the drawing to match the window size
+        ov->Draw(&render, w, h);
+        cairo_restore(cr);
+      }
+    }
+
+    return FALSE;
+  }
+
+  GdkPixbuf *pixbuf = nullptr;
+  GtkWidget *drawing_area = nullptr;
+  ImagePtr ptr;
+  bool needs_draw = true;
+  GtkWidget *window;
+  std::vector<OverlayBase *> *overlays = nullptr;
+  double scale_factor;
+
+  // flip the image rows on drawing
+  bool flip_ = false;
+
+  // clear per frame
+  bool clear_per_frame_ = true;
+};
+
+void DebugViewer::SetOverlays(std::vector<OverlayBase *> *overlays) {
+  self->overlays = overlays;
+}
+
+void DebugViewer::Redraw() {
+  if (!self->needs_draw) {
+    gtk_widget_queue_draw(self->drawing_area);
+    self->needs_draw = true;
+  }
+}
+
+void DebugViewer::UpdateImage(ImagePtr ptr) {
+  if (ptr.data() != self->ptr.data()) {
+    int w = ptr.fmt().w;
+    int h = ptr.fmt().h;
+    self->pixbuf = gdk_pixbuf_new_from_data(
+        (const unsigned char *)ptr.data(), GDK_COLORSPACE_RGB, FALSE, 8,
+        ptr.fmt().w, ptr.fmt().h, 3 * ptr.fmt().w, NULL, NULL);
+    self->ptr = ptr;
+
+    gtk_window_set_default_size(GTK_WINDOW(self->window), w * scale_factor,
+                                h * scale_factor);
+
+    gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
+                                h * scale_factor);
+    window_height_ = h;
+    window_width_ = w;
+  }
+}
+
+void DebugViewer::MoveTo(int x, int y) {
+  gtk_window_move(GTK_WINDOW(self->window), x, y);
+}
+
+void DebugViewer::SetScale(double scale_factor_inp) {
+  int w = window_width_;
+  int h = window_height_;
+
+  scale_factor = scale_factor_inp;
+  self->scale_factor = scale_factor;
+
+  gtk_window_resize(GTK_WINDOW(self->window), w * scale_factor,
+                    h * scale_factor);
+
+  gtk_widget_set_size_request(self->drawing_area, w * scale_factor,
+                              h * scale_factor);
+}
+
+gboolean debug_viewer_key_press_event(GtkWidget * /*widget*/,
+                                      GdkEventKey *event, gpointer user_data) {
+  auto &key_press_cb =
+      reinterpret_cast<DebugViewer *>(user_data)->key_press_event;
+  if (key_press_cb) key_press_cb(event->keyval);
+  return FALSE;
+}
+
+DebugViewer::DebugViewer(bool flip) : self(new Internals(flip)) {
+  self->scale_factor = scale_factor;
+  GtkWidget *window;
+  auto drawing_area = self->drawing_area = gtk_drawing_area_new();
+  gtk_widget_set_size_request(drawing_area, window_width_ * scale_factor,
+                              window_height_ * scale_factor);
+  gtk_widget_add_events(drawing_area, GDK_KEY_PRESS_MASK);
+
+  g_draw_signal_connect<DebugViewer::Internals, &DebugViewer::Internals::Draw>(
+      drawing_area, self.get());
+
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  self->window = window;
+  g_signal_connect(window, "key-press-event",
+                   G_CALLBACK(debug_viewer_key_press_event), this);
+  gtk_window_set_title(GTK_WINDOW(window), "Window");
+  gtk_window_set_default_size(GTK_WINDOW(window), window_width_ * scale_factor,
+                              window_height_ * scale_factor);
+
+  gtk_container_add(GTK_CONTAINER(window), drawing_area);
+  gtk_widget_show_all(window);
+}
+DebugViewer::~DebugViewer() {}
+
+void CairoRender::Text(int x, int y, int /*text_x*/, int /*text_y*/,
+                       const std::string &text) {
+  auto *pango_lay = pango_cairo_create_layout(cr_);
+  cairo_move_to(cr_, x, y);
+  pango_layout_set_text(pango_lay, text.data(), text.size());
+  pango_cairo_show_layout(cr_, pango_lay);
+  g_object_unref(pango_lay);
+}
+
+}  // namespace vision
+}  // namespace aos
diff --git a/aos/vision/debug/debug_viewer.h b/aos/vision/debug/debug_viewer.h
new file mode 100644
index 0000000..3bada28
--- /dev/null
+++ b/aos/vision/debug/debug_viewer.h
@@ -0,0 +1,78 @@
+#ifndef AOS_VISION_DEBUG_DEBUG_VIEWER_H_
+#define AOS_VISION_DEBUG_DEBUG_VIEWER_H_
+
+#include <cairo.h>
+#include <functional>
+#include "aos/vision/debug/overlay.h"
+#include "aos/vision/image/image_types.h"
+
+namespace aos {
+namespace vision {
+
+// Implement Cairo version of RenderInterface.
+class CairoRender : public RenderInterface {
+ public:
+  explicit CairoRender(cairo_t *cr) : cr_(cr) {}
+  virtual ~CairoRender() {}
+
+  void Translate(double x, double y) override { cairo_translate(cr_, x, y); }
+
+  void SetSourceRGB(double r, double g, double b) override {
+    cairo_set_source_rgb(cr_, r, g, b);
+  }
+
+  void MoveTo(double x, double y) override { cairo_move_to(cr_, x, y); }
+
+  void LineTo(double x, double y) override { cairo_line_to(cr_, x, y); }
+
+  void Circle(double x, double y, double r) override {
+    cairo_arc(cr_, x, y, r, 0.0, 2 * M_PI);
+  }
+
+  void Stroke() override { cairo_stroke(cr_); }
+
+  void Text(int x, int y, int text_x, int text_y,
+            const std::string &text) override;
+
+ private:
+  cairo_t *cr_;
+};
+
+// Simple debug view window.
+class DebugViewer {
+ public:
+  struct Internals;
+  explicit DebugViewer(bool flip);
+  ~DebugViewer();
+  // Explicit redraw queuing (Will not double-queue).
+  void Redraw();
+
+  // This will resize the window as well as updating to draw from the
+  // (not owned) ptr. When you change ptr, you should call Redraw();
+  void UpdateImage(ImagePtr ptr);
+
+  // Sets up the window to draw a list of overlays.
+  // See overlay.h for more info.
+  void SetOverlays(std::vector<OverlayBase *> *overlay);
+
+  // Resizes the window.
+  void SetScale(double scale_factor);
+
+  // Move window.
+  void MoveTo(int x, int y);
+
+  // Set to change the key_press behaviour.
+  // The argument type is a constant that looks like: GDK_KEY_#{key_val_name}
+  std::function<void(uint32_t)> key_press_event;
+
+ private:
+  double scale_factor = 1.0;
+  int window_width_ = 100;
+  int window_height_ = 100;
+  std::unique_ptr<Internals> self;
+};
+
+}  // namespace vision
+}  // namespace aos
+
+#endif  // AOS_VISION_DEBUG_DEBUG_VIEWER_H_
diff --git a/aos/vision/debug/overlay.h b/aos/vision/debug/overlay.h
new file mode 100644
index 0000000..fbd6838
--- /dev/null
+++ b/aos/vision/debug/overlay.h
@@ -0,0 +1,198 @@
+#ifndef _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_
+#define _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_
+
+#include <string>
+#include <vector>
+
+#include "aos/vision/image/image_types.h"
+#include "aos/vision/math/segment.h"
+#include "aos/vision/math/vector.h"
+
+namespace aos {
+namespace vision {
+
+// Abstract away rendering to avoid compiling gtk for arm.
+// This should match a reduced cairo rendering api.
+class RenderInterface {
+ public:
+  RenderInterface() {}
+  RenderInterface(RenderInterface &&other) = delete;
+  RenderInterface(const RenderInterface &other) = delete;
+  ~RenderInterface() {}
+
+  virtual void Translate(double x, double y) = 0;
+  virtual void SetSourceRGB(double r, double g, double b) = 0;
+  virtual void MoveTo(double x, double y) = 0;
+  virtual void LineTo(double x, double y) = 0;
+  virtual void Circle(double x, double y, double r) = 0;
+  // negative in x, y, text_x, text_y measures from max in those value
+  virtual void Text(int x, int y, int text_x, int text_y,
+                    const std::string &text) = 0;
+  virtual void Stroke() = 0;
+};
+
+// Interface for a list of overlays to be drawn onto a debug image.
+// These will be passed into the running vision algorithms to output debug info,
+// so they must not have costly side-effects.
+class OverlayBase {
+ public:
+  OverlayBase() {}
+  virtual ~OverlayBase() {}
+
+  // Draws this overlay to the given canvas.
+  virtual void Draw(RenderInterface *render, double /* width */,
+                    double /* height */) = 0;
+
+  // Clears the entire overlay.
+  virtual void Reset() = 0;
+
+  PixelRef color = {255, 0, 0};
+  double scale = 1.0;
+};
+
+// A lambda that renders directly to the render interface.
+class LambdaOverlay : public OverlayBase {
+ public:
+  std::function<void(RenderInterface *, double, double)> draw_fn;
+  void Draw(RenderInterface *render, double width, double height) override {
+    if (draw_fn) draw_fn(render, width, height);
+  }
+  void Reset() override {}
+};
+
+// Lines rendered in a coordinate system where the origin is the center
+// of the screen
+class LinesOverlay : public OverlayBase {
+ public:
+  LinesOverlay() : OverlayBase() {}
+  ~LinesOverlay() {}
+
+  // build a segment for this line
+  void add_line(Vector<2> st, Vector<2> ed) { add_line(st, ed, color); }
+
+  // build a segment for this line
+  void add_line(Vector<2> st, Vector<2> ed, PixelRef newColor) {
+    lines_.emplace_back(
+        std::pair<Segment<2>, PixelRef>(Segment<2>(st, ed), newColor));
+  }
+
+  void add_point(Vector<2> pt) { add_point(pt, color); }
+
+  // add a new point connected to the last point in the line
+  void add_point(Vector<2> pt, PixelRef newColor) {
+    if (lines_.empty()) {
+      lines_.emplace_back(
+          std::pair<Segment<2>, PixelRef>(Segment<2>(pt, pt), newColor));
+    } else {
+      Vector<2> st = lines_.back().first.B();
+      lines_.emplace_back(
+          std::pair<Segment<2>, PixelRef>(Segment<2>(st, pt), newColor));
+    }
+  }
+
+  void Draw(RenderInterface *render, double w, double h) override {
+    render->Translate(w / 2.0, h / 2.0);
+    for (const auto &ln : lines_) {
+      PixelRef localColor = ln.second;
+      render->SetSourceRGB(localColor.r / 255.0, localColor.g / 255.0,
+                           localColor.b / 255.0);
+      render->MoveTo(scale * ln.first.A().x(), -scale * ln.first.A().y());
+      render->LineTo(scale * ln.first.B().x(), -scale * ln.first.B().y());
+      render->Stroke();
+    }
+  }
+
+  // Empting the list will blank the whole overlay
+  void Reset() override { lines_.clear(); }
+
+ private:
+  // lines in this over lay
+  std::vector<std::pair<Segment<2>, PixelRef>> lines_;
+};
+
+// Lines rendered in pixel coordinates (Should match up with the screen.)
+class PixelLinesOverlay : public OverlayBase {
+ public:
+  PixelLinesOverlay() : OverlayBase() {}
+  ~PixelLinesOverlay() {}
+
+  // build a segment for this line
+  void add_line(Vector<2> st, Vector<2> ed) { add_line(st, ed, color); }
+
+  // build a segment for this line
+  void add_line(Vector<2> st, Vector<2> ed, PixelRef newColor) {
+    lines_.emplace_back(
+        std::pair<Segment<2>, PixelRef>(Segment<2>(st, ed), newColor));
+  }
+
+  void start_new_profile() { start_profile = true; }
+
+  // add a new point connected to the last point in the line
+  void add_point(Vector<2> pt, PixelRef newColor) {
+    if (lines_.empty() || start_profile) {
+      lines_.emplace_back(
+          std::pair<Segment<2>, PixelRef>(Segment<2>(pt, pt), newColor));
+      start_profile = false;
+    } else {
+      Vector<2> st = lines_.back().first.B();
+      lines_.emplace_back(
+          std::pair<Segment<2>, PixelRef>(Segment<2>(st, pt), newColor));
+    }
+  }
+
+  void Draw(RenderInterface *render, double /*width*/,
+            double /*hieght*/) override {
+    for (const auto &ln : lines_) {
+      PixelRef localColor = ln.second;
+      render->SetSourceRGB(localColor.r / 255.0, localColor.g / 255.0,
+                           localColor.b / 255.0);
+      render->MoveTo(ln.first.A().x(), ln.first.A().y());
+      render->LineTo(ln.first.B().x(), ln.first.B().y());
+      render->Stroke();
+    }
+  }
+
+  // Empting the list will blank the whole overlay.
+  void Reset() override { lines_.clear(); }
+
+ private:
+  // Lines in this overlay.
+  std::vector<std::pair<Segment<2>, PixelRef>> lines_;
+  bool start_profile = false;
+};
+
+// Circles rendered in a coordinate system where the origin is the center
+// of the screen.
+class CircleOverlay : public OverlayBase {
+ public:
+  CircleOverlay() : OverlayBase() {}
+  ~CircleOverlay() {}
+
+  // build a circle as a point and radius
+  std::pair<Vector<2>, double> *add_circle(Vector<2> center, double radius) {
+    circles_.emplace_back(std::pair<Vector<2>, double>(center, radius));
+    return &(circles_.back());
+  }
+
+  void Draw(RenderInterface *render, double w, double h) {
+    render->Translate(w / 2.0, h / 2.0);
+    render->SetSourceRGB(color.r / 255.0, color.g / 255.0, color.b / 255.0);
+    for (const auto &circle : circles_) {
+      render->Circle(scale * circle.first.x(), -scale * circle.first.y(),
+                     scale * circle.second);
+      render->Stroke();
+    }
+  }
+
+  // empting the list will blank the whole overlay
+  void Reset() { circles_.clear(); }
+
+ private:
+  // circles in this overlay
+  std::vector<std::pair<Vector<2>, double>> circles_;
+};
+
+}  // vision
+}  // aos
+
+#endif  // _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_
diff --git a/aos/vision/events/BUILD b/aos/vision/events/BUILD
index c3400fe..e003dc9 100644
--- a/aos/vision/events/BUILD
+++ b/aos/vision/events/BUILD
@@ -11,6 +11,14 @@
   ],
 )
 
+cc_library(name = "socket_types",
+    hdrs = ["socket_types.h"],
+    deps = [
+        "//aos/vision/events:tcp_server",
+        "//aos/vision/image:image_types",
+    ],
+)
+
 cc_library(
   name = 'intrusive_free_list',
   hdrs = ['intrusive_free_list.h'],
diff --git a/aos/vision/events/socket_types.h b/aos/vision/events/socket_types.h
new file mode 100644
index 0000000..2432cb9
--- /dev/null
+++ b/aos/vision/events/socket_types.h
@@ -0,0 +1,78 @@
+#ifndef _AOS_VISION_EVENTS_SOCKET_TYPES_H_
+#define _AOS_VISION_EVENTS_SOCKET_TYPES_H_
+
+#include <poll.h>
+#include <stdint.h>
+
+#include "aos/vision/events/tcp_server.h"
+#include "aos/vision/image/image_types.h"
+#include "google/protobuf/message.h"
+
+namespace aos {
+namespace events {
+
+// Simple TCP client connection that sends messages prefixed by length.
+// Useful to broadcast to a message all connected clients.
+class DataSocket : public events::SocketConnection {
+ public:
+  // Aliasing cast.
+  union data_len {
+    uint32_t len;
+    char buf[4];
+  };
+
+  DataSocket(events::TCPServerBase *serv, int fd)
+      : events::SocketConnection(serv, fd) {}
+
+  ~DataSocket() { printf("Closed connection on descriptor %d\n", fd()); }
+
+  void ReadEvent() override {
+    // Ignore reads, but don't leave them pending.
+    ssize_t count;
+    char buf[512];
+    while (true) {
+      count = read(fd(), &buf, sizeof buf);
+      if (count <= 0) return;
+    }
+  }
+
+  void Emit(const google::protobuf::Message &data) {
+    std::string d;
+    if (data.SerializeToString(&d)) {
+      Emit(d);
+    }
+  }
+
+  void Emit(vision::DataRef data) {
+    data_len len;
+    len.len = data.size();
+    int res = write(fd(), len.buf, sizeof len.buf);
+    if (res == -1) {
+      printf("Emit Error on write\n");
+    }
+    size_t write_count = 0;
+    while (write_count < data.size()) {
+      int len =
+          write(fd(), &data.data()[write_count], data.size() - write_count);
+      if (len == -1) {
+        if (errno == EAGAIN) {
+          struct pollfd waiting;
+          waiting.fd = fd();
+          waiting.events = POLLOUT;
+          poll(&waiting, 1, -1);
+        } else {
+          close(fd());
+          return;
+        }
+      } else {
+        write_count += len;
+      }
+      if (write_count != data.size()) printf("wrote: %d\n", len);
+    }
+  }
+};
+
+}  // namespace events
+}  // namespace aos
+
+#endif  // _AOS_VISION_EVENTS_SOCKET_TYPES_H_
diff --git a/aos/vision/events/udp.cc b/aos/vision/events/udp.cc
index 2d3d1d1..b5367f6 100644
--- a/aos/vision/events/udp.cc
+++ b/aos/vision/events/udp.cc
@@ -5,24 +5,24 @@
 #include "aos/common/logging/logging.h"
 
 namespace aos {
-namespace vision {
+namespace events {
 
-TXUdpSocket::TXUdpSocket(const char *ip_addr, int port)
+TXUdpSocket::TXUdpSocket(const std::string &ip_addr, int port)
     : fd_(PCHECK(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) {
   sockaddr_in destination_in;
   memset(&destination_in, 0, sizeof(destination_in));
   destination_in.sin_family = AF_INET;
   destination_in.sin_port = htons(port);
-  if (inet_aton(ip_addr, &destination_in.sin_addr) == 0) {
-    LOG(FATAL, "invalid IP address %s\n", ip_addr);
+  if (inet_aton(ip_addr.c_str(), &destination_in.sin_addr) == 0) {
+    LOG(FATAL, "invalid IP address %s\n", ip_addr.c_str());
   }
 
   PCHECK(connect(fd_.get(), reinterpret_cast<sockaddr *>(&destination_in),
                  sizeof(destination_in)));
 }
 
-int TXUdpSocket::Send(const void *data, int size) {
-  return PCHECK(send(fd_.get(), static_cast<const char *>(data), size, 0));
+int TXUdpSocket::Send(const char *data, int size) {
+  return PCHECK(send(fd_.get(), data, size, 0));
 }
 
 RXUdpSocket::RXUdpSocket(int port)
@@ -42,5 +42,5 @@
   return PCHECK(recv(fd_.get(), static_cast<char *>(data), size, 0));
 }
 
-}  // namespace vision
+}  // namespace events
 }  // namespace aos
diff --git a/aos/vision/events/udp.h b/aos/vision/events/udp.h
index a12a8bf..ed4a8e7 100644
--- a/aos/vision/events/udp.h
+++ b/aos/vision/events/udp.h
@@ -1,27 +1,28 @@
-#ifndef AOS_VISION_IMAGE_UDP_H_
-#define AOS_VISION_IMAGE_UDP_H_
+#ifndef AOS_VISION_EVENTS_UDP_H_
+#define AOS_VISION_EVENTS_UDP_H_
 
 #include <arpa/inet.h>
-#include <sys/socket.h>
 #include <math.h>
+#include <sys/socket.h>
 #include <unistd.h>
+#include <string>
 #include <vector>
 
 #include "aos/common/macros.h"
 #include "aos/common/scoped_fd.h"
 
 namespace aos {
-namespace vision {
+namespace events {
 
 // Simple wrapper around a transmitting UDP socket.
 //
 // LOG(FATAL)s for all errors, including from Send.
 class TXUdpSocket {
  public:
-  TXUdpSocket(const char *ip_addr, int port);
+  TXUdpSocket(const std::string &ip_addr, int port);
 
   // Returns the number of bytes actually sent.
-  int Send(const void *data, int size);
+  int Send(const char *data, int size);
 
  private:
   ScopedFD fd_;
@@ -45,7 +46,7 @@
   DISALLOW_COPY_AND_ASSIGN(RXUdpSocket);
 };
 
-}  // namespace vision
+}  // namespace events
 }  // namespace aos
 
-#endif  // AOS_VISION_IMAGE_UDP_H_
+#endif  // AOS_VISION_EVENTS_UDP_H_
diff --git a/aos/vision/events/udp_test.cc b/aos/vision/events/udp_test.cc
index 87046ba..7e77b68 100644
--- a/aos/vision/events/udp_test.cc
+++ b/aos/vision/events/udp_test.cc
@@ -3,7 +3,7 @@
 #include "gtest/gtest.h"
 
 namespace aos {
-namespace vision {
+namespace events {
 
 TEST(UDPTest, SendRecv) {
   RXUdpSocket rx(1109);
@@ -19,5 +19,5 @@
   EXPECT_EQ(txdata[3], rxdata[3]);
 }
 
-}  // namespace vision
+}  // namespace events
 }  // namespace aos
diff --git a/aos/vision/image/image_stream.h b/aos/vision/image/image_stream.h
index ca2d8be..bdfbc31 100644
--- a/aos/vision/image/image_stream.h
+++ b/aos/vision/image/image_stream.h
@@ -9,6 +9,8 @@
 namespace aos {
 namespace vision {
 
+// Converts a camera reader into a virtual base class that calls ProcessImage
+// on each new image.
 class ImageStreamEvent : public ::aos::events::EpollEvent {
  public:
   static std::unique_ptr<::camera::Reader> GetCamera(
@@ -17,28 +19,28 @@
     using namespace std::placeholders;
     std::unique_ptr<::camera::Reader> camread(new ::camera::Reader(
         fname,
-        std::bind(&ImageStreamEvent::ProcessHelper, obj, _1, _2), params);
+        std::bind(&ImageStreamEvent::ProcessHelper, obj, _1, _2), params));
     camread->StartAsync();
     return camread;
   }
 
   explicit ImageStreamEvent(std::unique_ptr<::camera::Reader> reader)
-      : ::aos::events::EpollEvent(reader->fd()), reader_(reader) {}
+      : ::aos::events::EpollEvent(reader->fd()), reader_(std::move(reader)) {}
 
   explicit ImageStreamEvent(const std::string &fname,
                             camera::CameraParams params)
       : ImageStreamEvent(GetCamera(fname, this, params)) {}
 
-  void ProcessHelper(DataRef data, uint64_t timestamp) {
+  void ProcessHelper(DataRef data, aos::monotonic_clock::time_point timestamp) {
     if (data.size() < 300) {
-      LOG(INFO, "got bad img: %d of size(%lu)\n", (int)timestamp, data.size());
+      LOG(INFO, "got bad img of size(%lu)\n", data.size());
       return;
     }
     ProcessImage(data, timestamp);
   }
-  virtual void ProcessImage(DataRef data, uint64_t timestamp) = 0;
+  virtual void ProcessImage(DataRef data, aos::monotonic_clock::time_point timestamp) = 0;
 
-  void ReadEvent(Context /*ctx*/) override { reader_->HandleFrame(); }
+  void ReadEvent() override { reader_->HandleFrame(); }
 
  private:
   std::unique_ptr<::camera::Reader> reader_;