[camera] add controller stream to camera_demo
This change add camera controller as an additional streaming source to
the camera manual test / demo. It also adds an extremely simple scenic
image node, populated with the text of the friendly name of the stream
source.
This change also fixes a bug in the legacy ISP driver that manifested in
the new camera_demo_test.
Test: ran the camera_demo_test
Change-Id: I082b1c47da8dc22182d49578452d30c8610160fd
diff --git a/src/camera/drivers/isp/mali-009/arm-isp.cc b/src/camera/drivers/isp/mali-009/arm-isp.cc
index 0a2473a..3c7f4ea 100644
--- a/src/camera/drivers/isp/mali-009/arm-isp.cc
+++ b/src/camera/drivers/isp/mali-009/arm-isp.cc
@@ -761,6 +761,7 @@
}
int ArmIspDevice::FrameProcessingThread() {
+ bool fr_frame_written = false;
while (running_frame_processing_.load()) {
sync_completion_wait(&frame_processing_signal_, ZX_TIME_INFINITE);
// Currently this is called only on the new frame signal, so we maintain
@@ -770,11 +771,13 @@
} else {
// Each of these calls has it's own interrupt, that it could be
// attached to:
- full_resolution_dma_->OnFrameWritten();
+ if (fr_frame_written) {
+ full_resolution_dma_->OnFrameWritten();
+ }
downscaled_dma_->OnFrameWritten();
}
// Now for the actions we should take on new frame:
- full_resolution_dma_->OnNewFrame();
+ fr_frame_written = full_resolution_dma_->OnNewFrame();
downscaled_dma_->OnNewFrame();
// Reset the signal
diff --git a/src/camera/drivers/isp/modules/dma-mgr.cc b/src/camera/drivers/isp/modules/dma-mgr.cc
index c542190..3e7ad6d5 100644
--- a/src/camera/drivers/isp/modules/dma-mgr.cc
+++ b/src/camera/drivers/isp/modules/dma-mgr.cc
@@ -195,10 +195,10 @@
}
// Called as one of the later steps when a new frame arrives.
-void DmaManager::OnNewFrame() {
+bool DmaManager::OnNewFrame() {
// If we have not initialized yet with a format, just skip.
if (!enabled_) {
- return;
+ return false;
}
// 1) Get another buffer
auto buffer = buffers_.LockBufferForWrite();
@@ -223,7 +223,7 @@
event.frame_status = fuchsia_camera_common_FrameStatus_ERROR_BUFFER_FULL;
event.metadata.timestamp = 0;
frame_available_callback_(event);
- return;
+ return false;
}
// 2) Optional? Set the DMA settings again... seems unnecessary
// 3) Set the DMA address
@@ -251,6 +251,8 @@
WriteFormat();
// Add buffer to queue of buffers we are writing:
write_locked_buffers_.push_front(std::move(*buffer));
+
+ return true;
}
zx_status_t DmaManager::ReleaseFrame(uint32_t buffer_index) {
diff --git a/src/camera/drivers/isp/modules/dma-mgr.h b/src/camera/drivers/isp/modules/dma-mgr.h
index 9283be5..a9552cf 100644
--- a/src/camera/drivers/isp/modules/dma-mgr.h
+++ b/src/camera/drivers/isp/modules/dma-mgr.h
@@ -51,7 +51,8 @@
Stream stream_type, std::unique_ptr<DmaManager>* out);
// Updates the dma writer with the address of a free buffer from the pool.
- void OnNewFrame();
+ // Returns true iff a new frame was actually written.
+ bool OnNewFrame();
// Signal that all consumers are done with this frame.
zx_status_t ReleaseFrame(uint32_t buffer_index);
diff --git a/src/camera/examples/demo/BUILD.gn b/src/camera/examples/demo/BUILD.gn
index eeee427..78fb6fd 100644
--- a/src/camera/examples/demo/BUILD.gn
+++ b/src/camera/examples/demo/BUILD.gn
@@ -15,6 +15,7 @@
deps = [
"./stream_provider",
+ "./text_node",
"//garnet/public/lib/fsl",
"//garnet/public/lib/ui/base_view/cpp",
"//garnet/public/lib/ui/scenic/cpp",
@@ -25,6 +26,7 @@
"//src/lib/ui/scenic/cpp",
"//src/ui/lib/glm_workaround",
"//zircon/public/lib/async-loop-cpp",
+ "//zircon/public/lib/fzl",
]
}
diff --git a/src/camera/examples/demo/main.cc b/src/camera/examples/demo/main.cc
index 82c5443..137bd9c 100644
--- a/src/camera/examples/demo/main.cc
+++ b/src/camera/examples/demo/main.cc
@@ -17,6 +17,7 @@
#include <lib/zx/time.h>
#include <stream_provider.h>
#include <sys/types.h>
+#include <text_node.h>
#include <queue>
#include <random>
@@ -54,8 +55,8 @@
}
// Draws a scenic scene containing a single rectangle with an image pipe material,
-// constructed with buffers populated by a stream provider (in this case, the ArmIspDevice).
-class DemoView : public scenic::BaseView, public fuchsia::camera2::Stream_EventSender {
+// constructed with buffers populated by a stream provider.
+class DemoView : public scenic::BaseView, public fuchsia::camera2::Stream::EventSender_ {
public:
explicit DemoView(scenic::ViewContext context, async::Loop* loop, bool chaos)
: BaseView(std::move(context), "Camera Demo"),
@@ -63,7 +64,8 @@
chaos_(chaos),
chaos_dist_(kChaosMaxSleepMsec,
static_cast<float>(kChaosMeanSleepMsec) / kChaosMaxSleepMsec),
- node_(session()) {}
+ node_(session()),
+ text_node_(session()) {}
~DemoView() override {
// Manually delete Wait instances before their corresponding events to avoid a failed assert.
@@ -78,16 +80,20 @@
bool chaos) {
auto view = std::make_unique<DemoView>(std::move(context), loop, chaos);
- auto stream_provider = StreamProvider::Create(StreamProvider::Source::ISP);
- if (!stream_provider) {
- FXL_LOG(ERROR) << "Failed to get ISP stream provider";
+ view->stream_provider_ = StreamProvider::Create(StreamProvider::Source::CONTROLLER);
+ if (!view->stream_provider_) {
+ FXL_LOG(ERROR) << "Failed to get CONTROLLER stream provider";
return nullptr;
}
fuchsia::sysmem::ImageFormat_2 format;
fuchsia::sysmem::BufferCollectionInfo_2 buffers;
- view->stream_ =
- stream_provider->ConnectToStream(view.get(), &format, &buffers, &view->should_rotate_);
+ view->stream_ = view->stream_provider_->ConnectToStream(view.get(), &format, &buffers,
+ &view->should_rotate_);
+ if (!view->stream_) {
+ FXL_LOG(ERROR) << "Failed to connect to stream";
+ return nullptr;
+ }
uint32_t image_pipe_id = view->session()->AllocResourceId();
view->session()->Enqueue(
@@ -95,16 +101,17 @@
scenic::Material material(view->session());
material.SetTexture(image_pipe_id);
view->session()->ReleaseResource(image_pipe_id);
- scenic::Rectangle shape(view->session(), format.display_width, format.display_height);
- view->shape_width_ = format.display_width;
- view->shape_height_ = format.display_height;
+ scenic::Rectangle shape(view->session(), format.coded_width, format.coded_height);
+ view->shape_width_ = format.coded_width;
+ view->shape_height_ = format.coded_height;
view->node_.SetShape(shape);
view->node_.SetMaterial(material);
view->root_node().AddChild(view->node_);
+ view->root_node().AddChild(view->text_node_);
fuchsia::images::ImageInfo image_info{};
- image_info.width = format.display_width;
- image_info.height = format.display_height;
+ image_info.width = format.coded_width;
+ image_info.height = format.coded_height;
image_info.stride = format.bytes_per_row;
image_info.pixel_format = convertFormat(format.pixel_format.type);
for (uint32_t i = 0; i < buffers.buffer_count; ++i) {
@@ -125,16 +132,22 @@
private:
// |scenic::BaseView|
void OnSceneInvalidated(fuchsia::images::PresentationInfo presentation_info) override {
- if (!has_logical_size())
+ if (!has_logical_size() || !has_metrics())
return;
if (should_rotate_) {
auto rotation = glm::angleAxis(glm::half_pi<float>(), glm::vec3(0, 0, 1));
node_.SetRotation(rotation.x, rotation.y, rotation.z, rotation.w);
}
- node_.SetTranslation(logical_size().x * 0.5f, logical_size().y * 0.5f, -5.0f);
+ node_.SetTranslation(logical_size().x * 0.5f, logical_size().y * 0.5f, -1.0f);
const float shape_vertical_size = should_rotate_ ? shape_width_ : shape_height_;
const float scale = logical_size().y / shape_vertical_size; // Fit vertically.
node_.SetScale(scale, scale, 1.0f);
+ text_node_.SetText(stream_provider_->GetName() +
+ (should_rotate_ ? " (Rotated by Scenic)" : ""));
+ text_node_.SetTranslation(logical_size().x * 0.5f + metrics().scale_x * 0.5f,
+ logical_size().y * 0.02f, -1.1f);
+ text_node_.SetScale(1.0f / metrics().scale_x, 1.0f / metrics().scale_y,
+ 1.0f / metrics().scale_z);
}
void OnInputEvent(fuchsia::ui::input::InputEvent event) override {
@@ -222,12 +235,14 @@
std::binomial_distribution<uint32_t> chaos_dist_;
std::unique_ptr<fuchsia::camera2::Stream> stream_;
scenic::ShapeNode node_;
+ TextNode text_node_;
fuchsia::images::ImagePipePtr image_pipe_;
std::map<uint32_t, uint32_t> image_ids_;
float shape_width_;
float shape_height_;
bool should_rotate_;
std::queue<std::pair<std::unique_ptr<async::Wait>, zx::event>> waiters_;
+ std::unique_ptr<StreamProvider> stream_provider_;
};
int main(int argc, const char** argv) {
diff --git a/src/camera/examples/demo/meta/demo.cmx b/src/camera/examples/demo/meta/demo.cmx
index 057da52..566e1cc 100644
--- a/src/camera/examples/demo/meta/demo.cmx
+++ b/src/camera/examples/demo/meta/demo.cmx
@@ -4,11 +4,13 @@
},
"sandbox": {
"dev": [
- "class/isp-device-test"
+ "class/isp-device-test",
+ "camera-controller"
],
"services": [
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
+ "fuchsia.sysmem.Allocator",
"fuchsia.tracing.provider.Registry",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
diff --git a/src/camera/examples/demo/stream_provider/BUILD.gn b/src/camera/examples/demo/stream_provider/BUILD.gn
index ea58016..8e8ba29 100644
--- a/src/camera/examples/demo/stream_provider/BUILD.gn
+++ b/src/camera/examples/demo/stream_provider/BUILD.gn
@@ -15,8 +15,9 @@
visibility = [ ":*" ]
sources = [
- "stream_provider.cc",
+ "controller_stream_provider.cc",
"isp_stream_provider.cc",
+ "stream_provider.cc",
]
deps = [
@@ -26,12 +27,14 @@
"//sdk/fidl/fuchsia.camera2.hal",
"//sdk/fidl/fuchsia.ui.gfx",
"//sdk/lib/fidl/cpp",
+ "//sdk/lib/sys/cpp",
"//src/lib/fxl",
"//src/lib/ui/scenic/cpp",
"//src/ui/lib/glm_workaround",
"//zircon/public/lib/async-loop-cpp",
"//zircon/system/fidl/fuchsia-camera-common",
"//zircon/system/fidl/fuchsia-camera-test",
+ "//zircon/system/fidl/fuchsia-hardware-camera",
]
configs += [
diff --git a/src/camera/examples/demo/stream_provider/controller_stream_provider.cc b/src/camera/examples/demo/stream_provider/controller_stream_provider.cc
new file mode 100644
index 0000000..deef0af
--- /dev/null
+++ b/src/camera/examples/demo/stream_provider/controller_stream_provider.cc
@@ -0,0 +1,197 @@
+// Copyright 2019 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.
+
+#include "controller_stream_provider.h"
+
+#include <fcntl.h>
+#include <fuchsia/hardware/camera/cpp/fidl.h>
+#include <lib/fdio/fdio.h>
+#include <lib/fzl/vmo-mapper.h>
+
+#include <fbl/unique_fd.h>
+#include <src/lib/fxl/logging.h>
+
+#include "streamptr_wrapper.h"
+
+static constexpr const char* kDevicePath = "/dev/camera-controller/camera-controller-device";
+
+ControllerStreamProvider::~ControllerStreamProvider() {
+ if (controller_ && streaming_) {
+ zx_status_t status = controller_->DisableStreaming();
+ if (status != ZX_OK) {
+ FXL_PLOG(WARNING, status) << "Failed to stop streaming via the controller";
+ }
+ }
+ if (buffer_collection_) {
+ zx_status_t status = buffer_collection_->Close();
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status);
+ }
+ }
+}
+
+std::unique_ptr<StreamProvider> ControllerStreamProvider::Create() {
+ auto provider = std::make_unique<ControllerStreamProvider>();
+
+ // Connect to sysmem.
+ zx_status_t status =
+ sys::ComponentContext::Create()->svc()->Connect(provider->allocator_.NewRequest());
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to connect to sysmem allocator service";
+ return nullptr;
+ }
+ if (!provider->allocator_) {
+ FXL_LOG(ERROR) << "Failed to connect to sysmem allocator service";
+ return nullptr;
+ }
+
+ // Connect to the controller device.
+ int result = open(kDevicePath, O_RDONLY);
+ if (result < 0) {
+ FXL_LOG(ERROR) << "Error opening " << kDevicePath;
+ return nullptr;
+ }
+ fbl::unique_fd controller_fd(result);
+ zx::channel channel;
+ status = fdio_get_service_handle(controller_fd.get(), channel.reset_and_get_address());
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to get service handle";
+ return nullptr;
+ }
+ fuchsia::hardware::camera::DevicePtr device;
+ device.Bind(std::move(channel));
+
+ // Connect to the controller interface.
+ device->GetChannel2(provider->controller_.NewRequest().TakeChannel());
+ if (!provider->controller_) {
+ FXL_LOG(ERROR) << "Failed to get controller interface from device";
+ return nullptr;
+ }
+
+ // Immediately enable streaming.
+ status = provider->controller_->EnableStreaming();
+ if (status != ZX_OK) {
+ FXL_LOG(WARNING) << "Failed to start streaming via the controller";
+ }
+ provider->streaming_ = true;
+
+ return std::move(provider);
+}
+
+// Offer a stream as served through the tester interface.
+std::unique_ptr<fuchsia::camera2::Stream> ControllerStreamProvider::ConnectToStream(
+ fuchsia::camera2::Stream::EventSender_* event_handler,
+ fuchsia::sysmem::ImageFormat_2* format_out,
+ fuchsia::sysmem::BufferCollectionInfo_2* buffers_out, bool* should_rotate_out) {
+ if (!format_out || !buffers_out || !should_rotate_out) {
+ return nullptr;
+ }
+
+ static constexpr const uint32_t kConfigIndex = 0;
+ static constexpr const uint32_t kStreamConfigIndex = 0;
+ static constexpr const uint32_t kImageFormatIndex = 0;
+
+ if (buffer_collection_.is_bound()) {
+ FXL_PLOG(ERROR, ZX_ERR_ALREADY_BOUND) << "Stream already bound by caller.";
+ return nullptr;
+ }
+
+ // Get the list of valid configs as reported by the controller.
+ fidl::VectorPtr<fuchsia::camera2::hal::Config> configs;
+ zx_status_t status_return = ZX_OK;
+ zx_status_t status = controller_->GetConfigs(&configs, &status_return);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to call GetConfigs";
+ return nullptr;
+ }
+ if (status_return != ZX_OK) {
+ FXL_PLOG(ERROR, status_return) << "Failed to get configs";
+ return nullptr;
+ }
+ if (configs->size() <= kConfigIndex) {
+ FXL_LOG(ERROR) << "Invalid config index " << kConfigIndex;
+ return nullptr;
+ }
+ auto& config = configs->at(kConfigIndex);
+ if (config.stream_configs.size() <= kStreamConfigIndex) {
+ FXL_LOG(ERROR) << "Invalid stream config index " << kStreamConfigIndex;
+ return nullptr;
+ }
+ auto& stream_config = config.stream_configs[kStreamConfigIndex];
+ if (stream_config.image_formats.size() <= kImageFormatIndex) {
+ FXL_LOG(ERROR) << "Invalid image format index " << kImageFormatIndex;
+ return nullptr;
+ }
+ auto& image_format = stream_config.image_formats[kImageFormatIndex];
+
+ // Attempt to create a buffer collection using controller-provided constraints.
+ if (!allocator_) {
+ FXL_LOG(ERROR) << "Allocator is dead!";
+ }
+ status = allocator_->AllocateNonSharedCollection(buffer_collection_.NewRequest());
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to allocate new collection";
+ return nullptr;
+ }
+ status = buffer_collection_->SetConstraints(true, stream_config.constraints);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to set constraints to those reported by the controller";
+ return nullptr;
+ }
+ status_return = ZX_OK;
+ fuchsia::sysmem::BufferCollectionInfo_2 buffers;
+ status = buffer_collection_->WaitForBuffersAllocated(&status_return, &buffers);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to call WaitForBuffersAllocated";
+ return nullptr;
+ }
+ if (status_return != ZX_OK) {
+ FXL_PLOG(ERROR, status_return) << "Failed to allocate buffers";
+ return nullptr;
+ }
+
+ // TODO(fxb/37296): remove ISP workarounds
+ // The ISP does not currently write the chroma layer, so initialize all VMOs to 128 (grayscale).
+ // This avoids the resulting image from appearing as 100% saturated green.
+ for (uint32_t i = 0; i < buffers.buffer_count; ++i) {
+ fzl::VmoMapper mapper;
+ status = mapper.Map(buffers.buffers[i].vmo, 0, buffers.settings.buffer_settings.size_bytes,
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Error mapping vmo";
+ return nullptr;
+ }
+ memset(mapper.start(), 128, mapper.size());
+ mapper.Unmap();
+ }
+
+ // Duplicate the collection info so it can be returned to the caller.
+ fuchsia::sysmem::BufferCollectionInfo_2 buffers_for_caller;
+ status = buffers.Clone(&buffers_for_caller);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to clone buffer collection";
+ return nullptr;
+ }
+
+ // Create the stream using the created buffer collection.
+ fuchsia::camera2::StreamPtr stream;
+ stream.set_error_handler(
+ [](zx_status_t status) { FXL_PLOG(ERROR, status) << "Server disconnected"; });
+ stream.events().OnFrameAvailable =
+ fit::bind_member(event_handler, &fuchsia::camera2::Stream::EventSender_::OnFrameAvailable);
+ status = controller_->CreateStream(kConfigIndex, kStreamConfigIndex, kImageFormatIndex,
+ std::move(buffers), stream.NewRequest());
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status) << "Failed to create stream";
+ return nullptr;
+ }
+
+ // The stream from controller is currently unrotated.
+ // TODO: once GDC is hooked up to do the rotation within the controller, set this to 'false'
+ *should_rotate_out = true;
+
+ *format_out = std::move(image_format);
+ *buffers_out = std::move(buffers_for_caller);
+ return std::make_unique<StreamPtrWrapper>(std::move(stream));
+}
diff --git a/src/camera/examples/demo/stream_provider/controller_stream_provider.h b/src/camera/examples/demo/stream_provider/controller_stream_provider.h
new file mode 100644
index 0000000..48a8237
--- /dev/null
+++ b/src/camera/examples/demo/stream_provider/controller_stream_provider.h
@@ -0,0 +1,30 @@
+// Copyright 2019 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.
+
+#ifndef SRC_CAMERA_EXAMPLES_DEMO_CONTROLLER_STREAM_PROVIDER_H_
+#define SRC_CAMERA_EXAMPLES_DEMO_CONTROLLER_STREAM_PROVIDER_H_
+
+#include <fuchsia/camera2/hal/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include "stream_provider.h"
+
+class ControllerStreamProvider : public StreamProvider {
+ public:
+ ~ControllerStreamProvider();
+ static std::unique_ptr<StreamProvider> Create();
+ virtual std::unique_ptr<fuchsia::camera2::Stream> ConnectToStream(
+ fuchsia::camera2::Stream::EventSender_* event_handler,
+ fuchsia::sysmem::ImageFormat_2* format_out,
+ fuchsia::sysmem::BufferCollectionInfo_2* buffers_out, bool* should_rotate_out) override;
+ virtual std::string GetName() override { return "fuchsia.camera2.Controller service"; }
+
+ private:
+ bool streaming_ = false;
+ fuchsia::camera2::hal::ControllerSyncPtr controller_;
+ fuchsia::sysmem::AllocatorSyncPtr allocator_;
+ fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection_;
+};
+
+#endif // SRC_CAMERA_EXAMPLES_DEMO_CONTROLLER_STREAM_PROVIDER_H_
diff --git a/src/camera/examples/demo/stream_provider/include/stream_provider.h b/src/camera/examples/demo/stream_provider/include/stream_provider.h
index e584c22..899826b 100644
--- a/src/camera/examples/demo/stream_provider/include/stream_provider.h
+++ b/src/camera/examples/demo/stream_provider/include/stream_provider.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_STREAM_PROVIDER_H_
-#define SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_STREAM_PROVIDER_H_
+#ifndef SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_INCLUDE_STREAM_PROVIDER_H_
+#define SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_INCLUDE_STREAM_PROVIDER_H_
#include <fuchsia/camera2/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
@@ -18,6 +18,7 @@
public:
enum class Source {
ISP,
+ CONTROLLER,
NUM_SOURCES,
};
virtual ~StreamProvider() = default;
@@ -52,4 +53,4 @@
fuchsia::sysmem::BufferCollectionInfo_2* buffers_out, bool* should_rotate_out) = 0;
};
-#endif // SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_STREAM_PROVIDER_H_
+#endif // SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_INCLUDE_STREAM_PROVIDER_H_
diff --git a/src/camera/examples/demo/stream_provider/isp_stream_provider.cc b/src/camera/examples/demo/stream_provider/isp_stream_provider.cc
index f1caf59..ca760873 100644
--- a/src/camera/examples/demo/stream_provider/isp_stream_provider.cc
+++ b/src/camera/examples/demo/stream_provider/isp_stream_provider.cc
@@ -7,7 +7,6 @@
#include <fcntl.h>
#include <fuchsia/camera/common/cpp/fidl.h>
#include <fuchsia/camera/test/cpp/fidl.h>
-#include <lib/async-loop/cpp/loop.h>
#include <lib/fdio/fdio.h>
#include <src/lib/fxl/logging.h>
diff --git a/src/camera/examples/demo/stream_provider/stream_provider.cc b/src/camera/examples/demo/stream_provider/stream_provider.cc
index c89940a..e195b39 100644
--- a/src/camera/examples/demo/stream_provider/stream_provider.cc
+++ b/src/camera/examples/demo/stream_provider/stream_provider.cc
@@ -4,12 +4,15 @@
#include "stream_provider.h"
+#include "controller_stream_provider.h"
#include "isp_stream_provider.h"
std::unique_ptr<StreamProvider> StreamProvider::Create(Source source) {
switch (source) {
case Source::ISP:
return IspStreamProvider::Create();
+ case Source::CONTROLLER:
+ return ControllerStreamProvider::Create();
default:
return nullptr;
}
diff --git a/src/camera/examples/demo/stream_provider/streamptr_wrapper.h b/src/camera/examples/demo/stream_provider/streamptr_wrapper.h
new file mode 100644
index 0000000..8d2a498
--- /dev/null
+++ b/src/camera/examples/demo/stream_provider/streamptr_wrapper.h
@@ -0,0 +1,36 @@
+// Copyright 2019 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.
+
+#ifndef SRC_CAMERA_EXAMPLES_DEMO_STREAMPTR_WRAPPER_H_
+#define SRC_CAMERA_EXAMPLES_DEMO_STREAMPTR_WRAPPER_H_
+
+#include <fuchsia/camera2/cpp/fidl.h>
+
+// This is a simple wrapper that forwards all Stream methods to an owned StreamPtr instance. It can
+// be used to serve the Stream interface as a std::unique_ptr.
+class StreamPtrWrapper : public fuchsia::camera2::Stream {
+ public:
+ StreamPtrWrapper(fuchsia::camera2::StreamPtr stream) : stream_(std::move(stream)) {}
+ fuchsia::camera2::StreamPtr& operator->() { return stream_; }
+ virtual void Start() override { stream_->Start(); }
+ virtual void Stop() override { stream_->Stop(); }
+ virtual void ReleaseFrame(uint32_t buffer_id) override { stream_->ReleaseFrame(buffer_id); }
+ virtual void AcknowledgeFrameError() override { stream_->AcknowledgeFrameError(); }
+ virtual void SetRegionOfInterest(float x_min, float y_min, float x_max, float y_max,
+ SetRegionOfInterestCallback callback) override {
+ stream_->SetRegionOfInterest(x_min, y_min, x_max, y_max, std::move(callback));
+ }
+ virtual void SetImageFormat(uint32_t image_format_index,
+ SetImageFormatCallback callback) override {
+ stream_->SetImageFormat(image_format_index, std::move(callback));
+ }
+ virtual void GetImageFormats(GetImageFormatsCallback callback) override {
+ stream_->GetImageFormats(std::move(callback));
+ }
+
+ private:
+ fuchsia::camera2::StreamPtr stream_;
+};
+
+#endif // SRC_CAMERA_EXAMPLES_DEMO_STREAMPTR_WRAPPER_H_
diff --git a/src/camera/examples/demo/test/meta/camera_demo_test.cmx b/src/camera/examples/demo/test/meta/camera_demo_test.cmx
index bb2b04f..ee48dc4 100644
--- a/src/camera/examples/demo/test/meta/camera_demo_test.cmx
+++ b/src/camera/examples/demo/test/meta/camera_demo_test.cmx
@@ -3,7 +3,8 @@
"fuchsia.test": {
"system-services": [
"fuchsia.ui.policy.Presenter",
- "fuchsia.ui.scenic.Scenic"
+ "fuchsia.ui.scenic.Scenic",
+ "fuchsia.sysmem.Allocator"
]
}
},
diff --git a/src/camera/examples/demo/text_node/BUILD.gn b/src/camera/examples/demo/text_node/BUILD.gn
new file mode 100644
index 0000000..5fd98cd
--- /dev/null
+++ b/src/camera/examples/demo/text_node/BUILD.gn
@@ -0,0 +1,49 @@
+# Copyright 2019 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.
+
+group("text_node") {
+ public_deps = [
+ ":text_node_lib",
+ ]
+ public_configs = [
+ ":text_node_config",
+ ]
+}
+
+source_set("text_node_lib") {
+ visibility = [ ":*" ]
+
+ sources = [
+ "text_node.cc",
+ "//zircon/system/ulib/gfx-font-data/9x16.c",
+ ]
+
+ include_dirs = [
+ "//zircon/system/ulib/gfx/include",
+ "//zircon/system/ulib/gfx-font-data/include",
+ ]
+
+ deps = [
+ "//src/lib/fxl",
+ "//src/lib/ui/scenic/cpp",
+ "//zircon/public/lib/fzl",
+ ]
+
+ configs += [
+ ":text_node_config",
+ ]
+
+ public_deps = [
+ "//sdk/fidl/fuchsia.camera2",
+ "//zircon/system/fidl/fuchsia-sysmem",
+ ]
+}
+
+config("text_node_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [
+ "//src/camera/examples/demo/text_node/include",
+ ]
+}
diff --git a/src/camera/examples/demo/text_node/include/text_node.h b/src/camera/examples/demo/text_node/include/text_node.h
new file mode 100644
index 0000000..40105d8
--- /dev/null
+++ b/src/camera/examples/demo/text_node/include/text_node.h
@@ -0,0 +1,21 @@
+// Copyright 2019 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.
+
+#ifndef SRC_CAMERA_EXAMPLES_DEMO_TEXT_NODE_INCLUDE_TEXT_NODE_H_
+#define SRC_CAMERA_EXAMPLES_DEMO_TEXT_NODE_INCLUDE_TEXT_NODE_H_
+
+#include <lib/ui/scenic/cpp/commands.h>
+#include <lib/ui/scenic/cpp/resources.h>
+
+class TextNode : public scenic::Node {
+ public:
+ explicit TextNode(scenic::Session* session);
+ TextNode(TextNode&& moved);
+ ~TextNode();
+
+ // Sets the text contents (lower 128 ASCII set) of the node.
+ zx_status_t SetText(const std::string s);
+};
+
+#endif // SRC_CAMERA_EXAMPLES_DEMO_TEXT_NODE_INCLUDE_TEXT_NODE_H_
diff --git a/src/camera/examples/demo/text_node/text_node.cc b/src/camera/examples/demo/text_node/text_node.cc
new file mode 100644
index 0000000..4989f66
--- /dev/null
+++ b/src/camera/examples/demo/text_node/text_node.cc
@@ -0,0 +1,98 @@
+// Copyright 2019 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.
+
+#include <lib/fzl/vmo-mapper.h>
+#include <lib/gfx-font-data/gfx-font-data.h>
+#include <text_node.h>
+
+#include <algorithm>
+
+#include <src/lib/fxl/logging.h>
+
+TextNode::TextNode(scenic::Session* session) : scenic::Node(session) {
+ session->Enqueue(scenic::NewCreateShapeNodeCmd(id()));
+}
+
+TextNode::TextNode(TextNode&& moved) : scenic::Node(std::move(moved)) {}
+
+TextNode::~TextNode() = default;
+
+zx_status_t TextNode::SetText(const std::string s) {
+ static constexpr const uint32_t kBitmapScale = 1; // Per-dimension pixel scatter ratio.
+ static constexpr const uint32_t kTextForeground = 0xFF000000; // Opaque black.
+ static constexpr const uint32_t kTextBackground = 0x7FFFFFFF; // Semi-transparent white.
+ static constexpr const struct {
+ uint8_t r = 0xFF;
+ uint8_t g = 0xFF;
+ uint8_t b = 0xFF;
+ uint8_t a = 0xFE;
+ } kShapeColor; // Nearly opaque white.
+
+ const auto& font = gfx_font_9x16;
+ static constexpr const uint32_t kFontDataBits = 8;
+
+ fuchsia::images::ImageInfo image_info;
+ uint32_t left_pad = font.width - kFontDataBits;
+ image_info.width = (font.width * s.size() + left_pad) * kBitmapScale;
+ image_info.height = font.height * kBitmapScale;
+ image_info.stride = image_info.width * sizeof(uint32_t);
+ image_info.alpha_format = fuchsia::images::AlphaFormat::NON_PREMULTIPLIED;
+ size_t image_size = image_info.width * image_info.height * sizeof(uint32_t);
+
+ zx::vmo vmo;
+ fzl::VmoMapper mapper;
+ zx_status_t status =
+ mapper.CreateAndMap(image_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo);
+ if (status != ZX_OK) {
+ FXL_PLOG(ERROR, status);
+ return status;
+ }
+
+ auto bitmap_data = reinterpret_cast<uint32_t*>(mapper.start());
+
+ size_t x_offset = left_pad * kBitmapScale;
+ for (uint32_t v = 0; v < image_info.height; ++v) {
+ for (uint32_t u = 0; u < x_offset; ++u) {
+ bitmap_data[image_info.width * v + u] = kTextBackground;
+ }
+ }
+ for (auto c : s) {
+ if (c & 0x80) { // Font only defines lower ASCII bitmaps.
+ c = '?';
+ }
+ const auto* char_data = &font.data[c * font.height];
+ for (uint32_t v = 0; v < font.height; ++v) {
+ for (uint32_t u = 0; u < font.width; ++u) {
+ uint32_t pixel = kTextBackground;
+ if (u < kFontDataBits && (char_data[v] & (1u << u))) {
+ pixel = kTextForeground;
+ }
+ for (uint32_t vv = 0; vv < kBitmapScale; ++vv) {
+ for (uint32_t uu = 0; uu < kBitmapScale; ++uu) {
+ bitmap_data[image_info.width * (v * kBitmapScale + vv) + u * kBitmapScale + uu +
+ x_offset] = pixel;
+ }
+ }
+ }
+ }
+ x_offset += font.width * kBitmapScale;
+ }
+
+ mapper.Unmap();
+
+ scenic::Memory memory(session(), std::move(vmo), image_size,
+ fuchsia::images::MemoryType::HOST_MEMORY);
+ scenic::Image image(memory, 0, fidl::Clone(image_info));
+ scenic::Material material(session());
+ material.SetTexture(image);
+ // TODO(fxb/38373): support is_alpha
+ material.SetColor(kShapeColor.r, kShapeColor.g, kShapeColor.b, kShapeColor.a);
+
+ scenic::Rectangle shape(session(), image_info.width * 1.0f / kBitmapScale,
+ image_info.height * 1.0f / kBitmapScale);
+ session()->Enqueue(scenic::NewSetShapeCmd(id(), shape.id()));
+ session()->Enqueue(scenic::NewSetMaterialCmd(id(), material.id()));
+
+ return ZX_OK;
+}