Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Unified Diff: media/capture/video/chromeos/arc_camera3_service.cc

Issue 2878233002: media: add ArcCamera3Service Mojo service (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/capture/video/chromeos/arc_camera3_service.cc
diff --git a/media/capture/video/chromeos/arc_camera3_service.cc b/media/capture/video/chromeos/arc_camera3_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4e27c2d9d625b29dbf043d15e91d0ab4b7effcc6
--- /dev/null
+++ b/media/capture/video/chromeos/arc_camera3_service.cc
@@ -0,0 +1,327 @@
+// Copyright 2017 The Chromium 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 "media/capture/video/chromeos/arc_camera3_service.h"
+
+#include <fcntl.h>
+#include <grp.h>
+#include <poll.h>
+#include <sys/uio.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+#include "mojo/edk/embedder/named_platform_handle_utils.h"
+#include "mojo/edk/embedder/pending_process_connection.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace media {
+
+namespace {
+
+const base::FilePath::CharType kArcCamera3SocketPath[] =
+ "/var/run/camera/camera3.sock";
+const char kArcCameraGroup[] = "arc-camera";
+
+// Creates a pipe. Returns true on success, otherwise false.
+// On success, |read_fd| will be set to the fd of the read side, and
+// |write_fd| will be set to the one of write side.
+static bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
+ int fds[2];
+ if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
+ PLOG(ERROR) << "pipe2 failed: ";
+ return false;
+ }
+
+ read_fd->reset(fds[0]);
+ write_fd->reset(fds[1]);
+ return true;
+}
+
+// Waits until |raw_socket_fd| is readable.
+// The operation may be cancelled originally triggered by user interaction to
+// disable ARC, or ARC instance is unexpectedly stopped (e.g. crash).
+// To notify such a situation, |raw_cancel_fd| is also passed to here, and the
+// write side will be closed in such a case.
+static bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
+ struct pollfd fds[2] = {
+ {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
+ };
+
+ if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
+ PLOG(ERROR) << "poll failed: ";
+ return false;
+ }
+
+ if (fds[1].revents) {
+ VLOG(1) << "Stop() was called";
+ return false;
+ }
+
+ DCHECK(fds[0].revents);
+ return true;
+}
+
+class MojoCameraClient : public ArcCamera3Service::CameraClientObserver {
+ public:
+ explicit MojoCameraClient(arc::mojom::ArcCamera3ClientPtr client)
+ : client_(std::move(client)) {}
+
+ void NotifyCameraHalRegistered(
+ arc::mojom::CameraModulePtr camera_module) override {
+ client_->NotifyCameraHalRegistered(std::move(camera_module));
+ }
+
+ arc::mojom::ArcCamera3ClientPtr& client() { return client_; }
+
+ private:
+ arc::mojom::ArcCamera3ClientPtr client_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MojoCameraClient);
+};
+
+} // namespace
+
+ArcCamera3Service::CameraHal::CameraHal(arc::mojom::ArcCamera3HalPtr camera_hal)
+ : camera_hal_(std::move(camera_hal)) {}
+
+void ArcCamera3Service::CameraHal::OpenCameraHal(
+ const base::Callback<void(arc::mojom::CameraModulePtr camera_module)>
+ callback) {
+ camera_hal_->OpenCameraHal(callback);
+}
+
+ArcCamera3Service::ArcCamera3Service()
+ : proxy_thread_("ProxyThread"), blocking_io_thread_("BlockingIOThread") {}
+
+ArcCamera3Service::~ArcCamera3Service() {
+ VLOG(1) << "Stopping ArcCamera3Service...";
+ proxy_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ArcCamera3Service::StopOnProxyThread,
+ base::Unretained(this)));
+ proxy_thread_.Stop();
+ blocking_io_thread_.Stop();
+ VLOG(1) << "ArcCamera3Service stopped";
+}
+
+// static
+ArcCamera3Service* ArcCamera3Service::GetInstance() {
+ return base::Singleton<ArcCamera3Service>::get();
+}
+
+bool ArcCamera3Service::Start() {
+ DCHECK(!proxy_thread_.IsRunning());
+ DCHECK(!blocking_io_thread_.IsRunning());
+
+ proxy_thread_.Start();
+ blocking_io_thread_.Start();
+ proxy_task_runner_ = proxy_thread_.task_runner();
+ blocking_io_task_runner_ = blocking_io_thread_.task_runner();
+
+ blocking_io_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ArcCamera3Service::CreateSocket, base::Unretained(this)));
+ return true;
+}
+
+void ArcCamera3Service::StopOnProxyThread() {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ base::DeleteFile(base::FilePath(kArcCamera3SocketPath),
+ /* recursive */ false);
+ // Close |cancel_pipe_| to quit the loop in WaitForIncomingConnection.
+ cancel_pipe_.reset();
+ client_observers_.clear();
+ camera_hal_.reset();
+ bindings_.clear();
+}
+
+void ArcCamera3Service::AddClientObserver(
+ std::unique_ptr<CameraClientObserver> observer) {
+ if (camera_hal_) {
+ proxy_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CameraHal::OpenCameraHal, camera_hal_->AsWeakPtr(),
+ base::Bind(&CameraClientObserver::NotifyCameraHalRegistered,
+ base::AsWeakPtr(observer.get()))));
+ }
+ client_observers_.insert(std::move(observer));
+}
+
+void ArcCamera3Service::RegisterCameraHal(
+ arc::mojom::ArcCamera3HalPtr camera_hal) {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ DCHECK(!camera_hal_);
+ camera_hal.set_connection_error_handler(base::Bind(
+ &ArcCamera3Service::OnCameraHalConnectionError, base::Unretained(this)));
+ camera_hal_.reset(new CameraHal(std::move(camera_hal)));
+ VLOG(1) << "Camera HAL registered";
+ for (auto& observer : client_observers_) {
+ proxy_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CameraHal::OpenCameraHal, camera_hal_->AsWeakPtr(),
+ base::Bind(&CameraClientObserver::NotifyCameraHalRegistered,
+ base::AsWeakPtr(observer.get()))));
+ }
+}
+
+void ArcCamera3Service::RegisterClient(arc::mojom::ArcCamera3ClientPtr client) {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ MojoCameraClient* camera_client = new MojoCameraClient(std::move(client));
+ std::unique_ptr<CameraClientObserver> client_observer(camera_client);
+ camera_client->client().set_connection_error_handler(base::Bind(
+ &ArcCamera3Service::OnMojoClientConnectionError, base::Unretained(this),
+ base::Unretained(client_observer.get())));
+ AddClientObserver(std::move(client_observer));
+ VLOG(1) << "New mojo client registered";
+}
+
+void ArcCamera3Service::CreateSocket() {
+ DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
+
+ base::FilePath socket_path(kArcCamera3SocketPath);
+ mojo::edk::ScopedPlatformHandle socket_fd = mojo::edk::CreateServerHandle(
+ mojo::edk::NamedPlatformHandle(socket_path.value()));
+ if (!socket_fd.is_valid()) {
+ LOG(ERROR) << "Failed to create the socket file: " << kArcCamera3SocketPath;
+ return;
+ }
+
+ // Change permissions on the socket.
+ struct group arc_camera_group;
+ struct group* result = nullptr;
+ char buf[1024];
+ if (HANDLE_EINTR(getgrnam_r(kArcCameraGroup, &arc_camera_group, buf,
+ sizeof(buf), &result)) < 0) {
+ PLOG(ERROR) << "getgrnam_r failed: ";
+ return;
+ }
+
+ if (!result) {
+ LOG(ERROR) << "Group '" << kArcCameraGroup << "' not found";
+ return;
+ }
+
+ if (HANDLE_EINTR(chown(kArcCamera3SocketPath, -1, arc_camera_group.gr_gid)) <
+ 0) {
+ PLOG(ERROR) << "chown failed: ";
+ return;
+ }
+
+ if (!base::SetPosixFilePermissions(socket_path, 0660)) {
+ PLOG(ERROR) << "Could not set permissions: " << socket_path.value() << ": ";
+ return;
+ }
+
+ blocking_io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ArcCamera3Service::CreateServiceLoop,
+ base::Unretained(this), base::Passed(&socket_fd)));
+}
+
+void ArcCamera3Service::CreateServiceLoop(
+ mojo::edk::ScopedPlatformHandle socket_fd) {
+ DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
+ DCHECK(!proxy_fd_.is_valid());
+ DCHECK(!cancel_pipe_.is_valid());
+ DCHECK(socket_fd.is_valid());
+
+ proxy_fd_ = std::move(socket_fd);
+
+ base::ScopedFD cancel_fd;
+ if (!CreatePipe(&cancel_fd, &cancel_pipe_)) {
+ LOG(ERROR) << "Failed to create cancel pipe";
+ return;
+ }
+
+ blocking_io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ArcCamera3Service::WaitForIncomingConnection,
+ base::Unretained(this), base::Passed(&cancel_fd)));
+ VLOG(1) << "ArcCamera3Service started";
+}
+
+void ArcCamera3Service::WaitForIncomingConnection(base::ScopedFD cancel_fd) {
+ DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
+ DCHECK(proxy_fd_.is_valid());
+ VLOG(1) << "Waiting for incoming connection...";
+
+ if (!proxy_fd_.is_valid()) {
+ LOG(ERROR) << "Invalid proxy fd";
+ return;
+ }
+
+ if (!WaitForSocketReadable(proxy_fd_.get().handle, cancel_fd.get())) {
+ VLOG(1) << "Quit ArcCamera3Service IO thread";
+ return;
+ }
+
+ mojo::edk::ScopedPlatformHandle accepted_fd;
+ if (mojo::edk::ServerAcceptConnection(proxy_fd_.get(), &accepted_fd, false) &&
+ accepted_fd.is_valid()) {
+ VLOG(1) << "Accepted a connection";
+ // Hardcode pid 0 since it is unused in mojo.
+ const base::ProcessHandle kUnusedChildProcessHandle = 0;
+ mojo::edk::PlatformChannelPair channel_pair;
+ mojo::edk::PendingProcessConnection process;
+ process.Connect(
+ kUnusedChildProcessHandle,
+ mojo::edk::ConnectionParams(channel_pair.PassServerHandle()));
+
+ mojo::edk::ScopedPlatformHandleVectorPtr handles(
+ new mojo::edk::PlatformHandleVector{
+ channel_pair.PassClientHandle().release()});
+
+ std::string token;
+ mojo::ScopedMessagePipeHandle message_pipe =
+ process.CreateMessagePipe(&token);
+
+ struct iovec iov = {const_cast<char*>(token.c_str()), token.length()};
+ ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
+ accepted_fd.get(), &iov, 1, handles->data(), handles->size());
+ if (result == -1) {
+ PLOG(ERROR) << "sendmsg failed: ";
+ } else {
+ proxy_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ArcCamera3Service::OnPeerConnected,
+ base::Unretained(this), base::Passed(&message_pipe)));
+ }
+ }
+
+ // Continue waiting for new connections.
+ blocking_io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ArcCamera3Service::WaitForIncomingConnection,
+ base::Unretained(this), base::Passed(&cancel_fd)));
+}
+
+void ArcCamera3Service::OnPeerConnected(
+ mojo::ScopedMessagePipeHandle message_pipe) {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ auto binding = base::MakeUnique<mojo::Binding<arc::mojom::ArcCamera3Service>>(
+ this, std::move(message_pipe));
+ bindings_.push_back(std::move(binding));
+ VLOG(1) << "New binding added";
+}
+
+void ArcCamera3Service::OnCameraHalConnectionError() {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ VLOG(1) << "Camera HAL connection lost";
+ camera_hal_.reset();
+}
+
+void ArcCamera3Service::OnMojoClientConnectionError(
+ CameraClientObserver* client_observer) {
+ DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+ for (auto& it : client_observers_) {
+ if (it.get() == client_observer) {
+ client_observers_.erase(it);
+ VLOG(1) << "Mojo client connection lost";
+ break;
+ }
+ }
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698