viddeo_device_stream almost there

Change-Id: Id9da503ba7e93ce952db81fcc2ce9f5af8f170ab
diff --git a/system/public/zircon/device/camera.h b/system/public/zircon/device/camera.h
index c3fab66..b5aa255 100644
--- a/system/public/zircon/device/camera.h
+++ b/system/public/zircon/device/camera.h
@@ -4,8 +4,11 @@
 
 #pragma once
 
+#include <assert.h>
+#include <zircon/compiler.h>
 #include <zircon/device/ioctl.h>
 #include <zircon/device/ioctl-wrapper.h>
+#include <zircon/types.h>
 
 #define CAMERA_IOCTL_GET_CHANNEL IOCTL(IOCTL_KIND_GET_HANDLE, IOCTL_FAMILY_CAMERA, 0)
 IOCTL_WRAPPER_OUT(ioctl_camera_get_channel, CAMERA_IOCTL_GET_CHANNEL, zx_handle_t);
diff --git a/system/ulib/video-utils/BUILD.gn b/system/ulib/video-utils/BUILD.gn
new file mode 100644
index 0000000..056f9e2
--- /dev/null
+++ b/system/ulib/video-utils/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("video-utils-config") {
+  include_dirs = [ "include" ]
+}
+
+source_set("video-utils") {
+  # Don't forget to update rules.mk as well for the Zircon build.
+  sources = [
+    "video_device_stream.cpp",
+    "include/video-utils/video-device-stream.h",
+  ]
+
+  public_deps = [
+    "//zircon/system/ulib/fbl",
+    "//zircon/system/ulib/zx",
+    "//zircon/system/ulib/async",
+    "//zircon/system/ulib/zxcpp",
+  ]
+
+  public_configs = [ ":video-utils-config" ]
+}
diff --git a/system/ulib/video-utils/include/video-utils/video_device_stream.h b/system/ulib/video-utils/include/video-utils/video_device_stream.h
index dca70b4..2a35a78 100644
--- a/system/ulib/video-utils/include/video-utils/video_device_stream.h
+++ b/system/ulib/video-utils/include/video-utils/video_device_stream.h
@@ -5,11 +5,13 @@
 #pragma once
 
 #include <zircon/device/camera.h>
+// #include <camera-driver-proto/camera-proto.h>
 #include <zircon/types.h>
 #include <zx/channel.h>
 #include <zx/vmo.h>
 #include <fbl/unique_ptr.h>
 #include <fbl/vector.h>
+#include <async/auto_wait.h>
 
 namespace video {
 namespace utils {
@@ -18,53 +20,35 @@
 public:
     zx_status_t Open();
     zx_status_t GetSupportedFormats(fbl::Vector<camera_video_format_t>* out_formats) const;
-    zx_status_t SetFormat(camera_video_format_t format, buffer_layout_t layout, fbl::Vector<zx::event> &release_fences);
+    zx_status_t SetFormat(camera_video_format_t format);
+    zx_status_t SetBuffer(const zx::vmo &vmo);
     zx_status_t StartRingBuffer();
     zx_status_t StopRingBuffer();
+    void OnNewFrame(camera_vb_frame_notify_t frame_info);
+    void ReleaseFrame(uint64_t data_offset);
     void        ResetRingBuffer();
     void        Close();
 
-    bool IsStreamBufChannelConnected() const { return IsChannelConnected(stream_ch_); }
-    bool IsRingBufChannelConnected() const { return IsChannelConnected(rb_ch_); }
-
-    const char* name()                 const { return name_; }
-    bool        input()                const { return input_; }
-    uint32_t    frame_rate()           const { return frame_rate_; }
-    uint32_t    sample_size()          const { return sample_size_; }
-    uint32_t    channel_cnt()          const { return channel_cnt_; }
-    uint32_t    frame_sz()             const { return frame_sz_; }
-    uint64_t    fifo_depth()           const { return fifo_depth_; }
-    uint32_t    ring_buffer_bytes()    const { return rb_sz_; }
-    void*       ring_buffer()          const { return rb_virt_; }
-    uint64_t    start_time()           const { return start_time_; }
-    uint64_t    external_delay_nsec()  const { return external_delay_nsec_; }
-
 protected:
-    friend class fbl::unique_ptr<AudioDeviceStream>;
+    // friend class fbl::unique_ptr<AudioDeviceStream>;
 
-    static bool IsChannelConnected(const zx::channel& ch);
+    // static bool IsChannelConnected(const zx::channel& ch);
 
-    AudioDeviceStream(bool input, uint32_t dev_id);
-    AudioDeviceStream(bool input, const char* dev_path);
-    virtual ~AudioDeviceStream();
+    // The maximum size a frame will occupy in the video stream.
+    // A value of zero means that the video buffer channel is uninitialized.
+    uint32_t max_frame_size_ = 0;
+
+    VideoDeviceStream(bool input, uint32_t dev_id);
+    VideoDeviceStream(bool input, const char* dev_path);
+    virtual ~VideoDeviceStream();
 
     zx::channel stream_ch_;
-    zx::channel rb_ch_;
-    zx::vmo     rb_vmo_;
+    zx::channel vb_ch_;
 
+    async::AutoWait new_frame_waiter_;
     const bool  input_;
     char        name_[64] = { 0 };
 
-    audio_sample_format_t sample_format_;
-    uint64_t start_time_           = 0;
-    uint64_t external_delay_nsec_  = 0;
-    uint32_t frame_rate_           = 0;
-    uint32_t sample_size_          = 0;
-    uint32_t channel_cnt_          = 0;
-    uint32_t frame_sz_             = 0;
-    uint32_t fifo_depth_           = 0;
-    uint32_t rb_sz_                = 0;
-    void*    rb_virt_              = nullptr;
 };
 
 }  // namespace utils
diff --git a/system/ulib/video-utils/rules.mk b/system/ulib/video-utils/rules.mk
new file mode 100644
index 0000000..cf5ba7c
--- /dev/null
+++ b/system/ulib/video-utils/rules.mk
@@ -0,0 +1,22 @@
+# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := userlib
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/video_device_stream.cpp
+
+MODULE_STATIC_LIBS := \
+    system/ulib/zx \
+    system/ulib/fdio \
+    system/ulib/async \
+    system/ulib/fbl
+
+MODULE_PACKAGE := src
+
+include make/module.mk
diff --git a/system/ulib/video-utils/video_device_stream.cpp b/system/ulib/video-utils/video_device_stream.cpp
index 314360f..a558964 100644
--- a/system/ulib/video-utils/video_device_stream.cpp
+++ b/system/ulib/video-utils/video_device_stream.cpp
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <audio-utils/audio-device-stream.h>
-#include <audio-utils/audio-input.h>
-#include <audio-utils/audio-output.h>
+#include <video-utils/video_device_stream.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <zircon/assert.h>
@@ -22,7 +20,7 @@
 #include <stdio.h>
 #include <string.h>
 
-namespace audio {
+namespace video {
 namespace utils {
 
 static constexpr zx_duration_t CALL_TIMEOUT = ZX_MSEC(500);
@@ -94,12 +92,12 @@
 }
 
 VideoDeviceStream::VideoDeviceStream(bool input, uint32_t dev_id)
-    : input_(input) {
+  : new_frame_waiter_(fsl::MessageLoop::GetCurrent()->async()), input_(input) {
     snprintf(name_, sizeof(name_), "/dev/class/camera/%03u", dev_id);
 }
 
 VideoDeviceStream::VideoDeviceStream(bool input, const char* dev_path)
-    : input_(input) {
+  : new_frame_waiter_(fsl::MessageLoop::GetCurrent()->async()), input_(input) {
     strncpy(name_, dev_path, sizeof(name_));
     name_[sizeof(name_) - 1] = 0;
 }
@@ -112,9 +110,9 @@
     if (stream_ch_ != ZX_HANDLE_INVALID)
         return ZX_ERR_BAD_STATE;
 
-    int fd = ::open(name(), O_RDONLY);
+    int fd = ::open(name_, O_RDONLY);
     if (fd < 0) {
-        printf("Failed to open \"%s\" (res %d)\n", name(), fd);
+        printf("Failed to open \"%s\" (res %d)\n", name_, fd);
         return fd;
     }
 
@@ -144,7 +142,7 @@
 
     req.hdr.cmd = CAMERA_STREAM_CMD_GET_FORMATS;
     res = DoCallImpl(stream_ch_, req, &resp, nullptr, &rxed);
-    if ((res != ZX_OK) || (rxed < MIN_RESP_SIZE)) {
+    if ((res != ZX_OK)) {
         printf("Failed to fetch initial suppored format list chunk (res %d, rxed %u)\n",
                 res, rxed);
         return res;
@@ -165,9 +163,8 @@
     }
 
     for (uint16_t i = 0; i < expected_formats; ++i) {
-        out_formats->push_back(resp.format_ranges[i]);
+        out_formats->push_back(resp.formats[i]);
     }
-    zx_txid_t txid = resp.hdr.transaction_id;
     uint32_t processed_formats = 0;
     
 
@@ -218,53 +215,60 @@
 }
 
 
-zx_status_t VideoDeviceStream::SetFormat(camera_video_format_t format, buffer_layout_t layout, 
-                                         fbl::Vector<zx::event> &release_fences) {
-    if ((stream_ch_ == ZX_HANDLE_INVALID) || (rb_ch_ != ZX_HANDLE_INVALID))
+zx_status_t VideoDeviceStream::SetFormat(camera_video_format_t format) {
+    if ((stream_ch_ == ZX_HANDLE_INVALID) || (vb_ch_ != ZX_HANDLE_INVALID))
         return ZX_ERR_BAD_STATE;
 
+    camera_stream_cmd_set_format_req_t  req;
+    camera_stream_cmd_set_format_resp_t resp;
 
-    channel_cnt_   = channels;
-    frame_sz_      = channels * sample_size_;
-    frame_rate_    = frames_per_second;
-    sample_format_ = sample_format;
+    req.hdr.cmd = CAMERA_STREAM_CMD_SET_FORMAT;
+    req.video_format = format;
+    zx::handle resp_handle_out;
 
-    camera_stream_cmd_configure_stream_req_t req;
-    camera_stream_cmd_configure_stream_resp_t resp;
-    req.hdr.cmd                    = CAMERA_STREAM_CMD_CONFIGURE_STREAM;
-    req.total_frame_entries_count  = release_fences.size();
-    req.already_sent_count         = 0;
-    req.video_format               = format;
-    for (uint16_t i = 0; i < release_fences.size(); ++i) {
-        req.frame_entries[i].offset = layout.buffer_offsets[i];
-        req.frame_entries[i].size   = layout.buffer_sizes[i];
+    zx_status_t res = DoCall(stream_ch_, req, &resp, &resp_handle_out);
+    
+    if (res != ZX_OK) {
+        printf("Failed to set format %u/%uHz Res: %ux%u @ %u bpp  (res %d)\n",
+               format.frames_per_sec_numerator, 
+               format.frames_per_sec_denominator, format.width, format.height,
+               format.bits_per_pixel, res);
+        return res;
     }
-    //Set the frame entries, up to CAMERA_STREAM_CMD_CONFIGURE_STREAM_MAX_FRAME_ENTRIES
-    uint16_t handles_out = release_fences.size() + 2;
 
-     zx_status_t DoCallImpl(const zx::channel& channel,
-                       const ReqType&     req,
-                       RespType*          resp,
-                       zx::handle*        resp_handle_out,
-                       uint32_t*          resp_len_out = nullptr) {
+    max_frame_size_ = resp.max_frame_size;
+
+    // TODO(garratt) : Verify the type of this handle before transferring it to
+    // our ring buffer channel handle.
+    vb_ch_.reset(resp_handle_out.release());
+
+    return res;
+}
+
+zx_status_t VideoDeviceStream::SetBuffer(const zx::vmo &buffer_vmo) {
+
+    ZX_DEBUG_ASSERT(vb_ch_);
+    camera_vb_cmd_set_buffer_req_t req;
+    req.hdr.cmd = CAMERA_VB_CMD_SET_BUFFER;
+    camera_vb_cmd_set_buffer_resp_t resp;
+    zx_handle_t vmo_handle;
+    //TODO(garratt): check this:
+    zx_handle_duplicate(buffer_vmo.get(), ZX_RIGHT_SAME_RIGHTS, &vmo_handle);
     zx_channel_call_args_t args;
-
-    ZX_DEBUG_ASSERT((resp_handle_out == nullptr) || !resp_handle_out->is_valid());
-
     args.wr_bytes       = &req;
-    args.wr_num_bytes   = sizeof(camera_stream_cmd_configure_stream_req_t);
-    args.wr_handles     = nullptr; // TODO(garratt)
-    args.wr_num_handles = handles_out;
+    args.wr_num_bytes   = sizeof(camera_vb_cmd_set_buffer_req_t);
+    args.wr_handles     = &vmo_handle;
+    args.wr_num_handles = 1;
     args.rd_bytes       = &resp;
-    args.rd_num_bytes   = sizeof(camera_stream_cmd_configure_stream_resp_t);
+    args.rd_num_bytes   = sizeof(camera_vb_cmd_set_buffer_resp_t);
     args.rd_handles     = nullptr;
     args.rd_num_handles = 0;
 
     uint32_t bytes, handles;
     zx_status_t read_status, write_status;
 
-    write_status = stream_ch_.call(0, zx_deadline_after(CALL_TIMEOUT), &args, &bytes, &handles,
-                                &read_status);
+    write_status = vb_ch_.call(0, zx_deadline_after(CALL_TIMEOUT), &args, 
+            &bytes, &handles, &read_status);
 
     if (write_status != ZX_OK) {
         if (write_status == ZX_ERR_CALL_FAILED) {
@@ -275,69 +279,55 @@
             return write_status;
         }
     }
-    
-    
-    
-    zx::handle tmp;
-    zx_status_t res = DoCall(stream_ch_, req, &resp, &tmp);
-    if (res != ZX_OK) {
-        printf("Failed to set format %uHz %hu-Ch fmt 0x%x (res %d)\n",
-                frames_per_second, channels, sample_format, res);
+
+    // Make sure that the number of bytes we got back matches the size of the
+    // response structure.
+    if (bytes != sizeof(camera_vb_cmd_set_buffer_resp_t)) {
+        printf("Unexpected response size (got %u, expected %zu)\n", bytes, sizeof(camera_vb_cmd_set_buffer_resp_t));
+        return ZX_ERR_INTERNAL;
     }
+    // zx_status_t result = dynamic_cast<camera_vb_cmd_set_buffer_resp_t*>(&resp)->result;
+    if (ZX_OK != resp.result) {
+        printf("SetBuffer failure (result: %d)\n", resp.result);
 
-    external_delay_nsec_ = resp.external_delay_nsec;
+    }
+    new_frame_waiter_.set_object(vb_ch_.get());
+    new_frame_waiter_.set_trigger(ZX_CHANNEL_READABLE);
+    new_frame_waiter_.set_handler(fbl::BindMember(this, &VideoDeviceStream::OnNewMessageSignalled));
+    auto status = new_frame_waiter_.Begin();
+    FXL_DCHECK(status == ZX_OK);
 
-    // TODO(johngro) : Verify the type of this handle before transferring it to
-    // our ring buffer channel handle.
-    rb_ch_.reset(tmp.release());
-
-    return res;
+    return ZX_OK;
 }
 
-
 zx_status_t VideoDeviceStream::StartRingBuffer() {
-    if (rb_ch_ == ZX_HANDLE_INVALID)
+    if (vb_ch_ == ZX_HANDLE_INVALID)
         return ZX_ERR_BAD_STATE;
 
-    audio_rb_cmd_start_req_t  req;
-    audio_rb_cmd_start_resp_t resp;
+    camera_vb_cmd_start_req_t  req;
+    camera_vb_cmd_start_resp_t resp;
 
-    req.hdr.cmd = AUDIO_RB_CMD_START;
-    req.hdr.transaction_id = 1;
+    req.hdr.cmd = CAMERA_VB_CMD_START;
 
-    zx_status_t res = DoCall(rb_ch_, req, &resp);
-
-    if (res == ZX_OK) {
-        start_time_ = resp.start_time;
-    }
+    zx_status_t res = DoCall(vb_ch_, req, &resp);
 
     return res;
 }
 
 zx_status_t VideoDeviceStream::StopRingBuffer() {
-    if (rb_ch_ == ZX_HANDLE_INVALID)
+    if (vb_ch_ == ZX_HANDLE_INVALID)
         return ZX_ERR_BAD_STATE;
 
-    start_time_ = 0;
+    camera_vb_cmd_stop_req_t  req;
+    camera_vb_cmd_stop_resp_t resp;
 
-    audio_rb_cmd_stop_req_t  req;
-    audio_rb_cmd_stop_resp_t resp;
+    req.hdr.cmd = CAMERA_VB_CMD_STOP;
 
-    req.hdr.cmd = AUDIO_RB_CMD_STOP;
-    req.hdr.transaction_id = 1;
-
-    return DoCall(rb_ch_, req, &resp);
+    return DoCall(vb_ch_, req, &resp);
 }
 
 void VideoDeviceStream::ResetRingBuffer() {
-    if (rb_virt_ != nullptr) {
-        ZX_DEBUG_ASSERT(rb_sz_ != 0);
-        zx::vmar::root_self().unmap(reinterpret_cast<uintptr_t>(rb_virt_), rb_sz_);
-    }
-    rb_ch_.reset();
-    rb_vmo_.reset();
-    rb_sz_ = 0;
-    rb_virt_ = nullptr;
+    vb_ch_.reset();
 }
 
 void VideoDeviceStream::Close() {
@@ -345,14 +335,40 @@
     stream_ch_.reset();
 }
 
-bool VideoDeviceStream::IsChannelConnected(const zx::channel& ch) {
-    if (!ch.is_valid())
-        return false;
-
-    zx_signals_t junk;
-    return ch.wait_one(ZX_CHANNEL_PEER_CLOSED, 0u, &junk) != ZX_ERR_TIMED_OUT;
+  // This function is called when the release fence is signalled
+async_wait_result_t VideoDeviceStream::OnNewMessageSignalled(async_t* async, zx_status_t status,
+                            const zx_packet_signal* signal) {
+    if (status != ZX_OK) {
+      FXL_LOG(ERROR) << "VideoDeviceStream received an error ("
+                     << zx_status_get_string(status) << ").  Exiting.";
+      return ASYNC_WAIT_FINISHED;
+    }
+    // todo: does the signal reset itself?
+    // Read channel
+    uint32_t rxed;
+    camera_vb_frame_notify_t resp; 
+    zx_status_t res = stream_ch_.read(0u, &resp, sizeof(resp), &rxed, nullptr, 0, nullptr);
+    if (res != ZX_OK) {
+       FXL_LOG(ERROR) << "Failed to read notify";
+       return ASYNC_WAIT_AGAIN;
+    }
+    if (resp.hdr != CAMERA_VB_FRAME_NOTIFY) {
+        FXL_LOG(ERROR) << "Wrong message on the channel";
+        return ASYNC_WAIT_AGAIN;
+    }
+    OnNewFrame(resp);
+    // Call OnNewFrame
+    return ASYNC_WAIT_AGAIN;
 }
 
+// bool VideoDeviceStream::IsChannelConnected(const zx::channel& ch) {
+    // if (!ch.is_valid())
+        // return false;
+
+    // zx_signals_t junk;
+    // return ch.wait_one(ZX_CHANNEL_PEER_CLOSED, 0u, &junk) != ZX_ERR_TIMED_OUT;
+// }
+
 
 }  // namespace utils
 }  // namespace audio