summaryrefslogtreecommitdiff
path: root/src/apps/cam/camera_session.h
blob: 4442fd9b1a6a3a5c4d65bb53328ab2902b9c2cea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * Camera capture session
 */

#pragma once

#include <memory>
#include <stdint.h>
#include <string>
#include <vector>

#include <libcamera/base/signal.h>

#include <libcamera/camera.h>
#include <libcamera/camera_manager.h>
#include <libcamera/framebuffer.h>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>

#include "../common/options.h"

class CaptureScript;
class FrameSink;

class CameraSession
{
public:
	CameraSession(libcamera::CameraManager *cm,
		      const std::string &cameraId, unsigned int cameraIndex,
		      const OptionsParser::Options &options);
	~CameraSession();

	bool isValid() const { return config_ != nullptr; }
	const OptionsParser::Options &options() { return options_; }

	libcamera::Camera *camera() { return camera_.get(); }
	libcamera::CameraConfiguration *config() { return config_.get(); }

	void listControls() const;
	void listProperties() const;
	void infoConfiguration() const;

	int start();
	void stop();

	libcamera::Signal<> captureDone;

private:
	int startCapture();

	int queueRequest(libcamera::Request *request);
	void requestComplete(libcamera::Request *request);
	void processRequest(libcamera::Request *request);
	void sinkRelease(libcamera::Request *request);

	const OptionsParser::Options &options_;
	std::shared_ptr<libcamera::Camera> camera_;
	std::unique_ptr<libcamera::CameraConfiguration> config_;

	std::unique_ptr<CaptureScript> script_;

	std::map<const libcamera::Stream *, std::string> streamNames_;
	std::unique_ptr<FrameSink> sink_;
	unsigned int cameraIndex_;

	uint64_t last_;

	unsigned int queueCount_;
	unsigned int captureCount_;
	unsigned int captureLimit_;
	bool printMetadata_;

	std::unique_ptr<libcamera::FrameBufferAllocator> allocator_;
	std::vector<std::unique_ptr<libcamera::Request>> requests_;
};
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * ipc_unixsocket.cpp - IPC mechanism based on Unix sockets
 */

#include "libcamera/internal/ipc_unixsocket.h"

#include <array>
#include <poll.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#include <libcamera/base/event_notifier.h>
#include <libcamera/base/log.h>

/**
 * \file ipc_unixsocket.h
 * \brief IPC mechanism based on Unix sockets
 */

namespace libcamera {

LOG_DEFINE_CATEGORY(IPCUnixSocket)

/**
 * \struct IPCUnixSocket::Payload
 * \brief Container for an IPC payload
 *
 * Holds an array of bytes and an array of file descriptors that can be
 * transported across a IPC boundary.
 */

/**
 * \var IPCUnixSocket::Payload::data
 * \brief Array of bytes to cross IPC boundary
 */

/**
 * \var IPCUnixSocket::Payload::fds
 * \brief Array of file descriptors to cross IPC boundary
 */

/**
 * \class IPCUnixSocket
 * \brief IPC mechanism based on Unix sockets
 *
 * The Unix socket IPC allows bidirectional communication between two processes
 * through unnamed Unix sockets. It implements datagram-based communication,
 * transporting entire payloads with guaranteed ordering.
 *
 * The IPC design is asynchronous, a message is queued to a receiver which gets
 * notified that a message is ready to be consumed by the \ref readyRead
 * signal. The sender of the message gets no notification when a message is
 * delivered nor processed. If such interactions are needed a protocol specific
 * to the users use-case should be implemented on top of the IPC objects.
 *
 * Establishment of an IPC channel is asymmetrical. The side that initiates
 * communication first instantiates a local side socket and creates the channel
 * with create(). The function returns a file descriptor for the remote side of
 * the channel, which is passed to the remote process through an out-of-band
 * communication method. The remote side then instantiates a socket, and binds
 * it to the other side by passing the file descriptor to bind(). At that point
 * the channel is operation and communication is bidirectional and symmmetrical.
 *
 * \context This class is \threadbound.
 */

IPCUnixSocket::IPCUnixSocket()
	: headerReceived_(false), notifier_(nullptr)
{
}

IPCUnixSocket::~IPCUnixSocket()
{
	close();
}

/**
 * \brief Create an new IPC channel
 *
 * This function creates a new IPC channel. The socket instance is bound to the
 * local side of the channel, and the function returns a file descriptor bound
 * to the remote side. The caller is responsible for passing the file descriptor
 * to the remote process, where it can be used with IPCUnixSocket::bind() to
 * bind the remote side socket.
 *
 * \return A file descriptor. It is valid on success or invalid otherwise.
 */
UniqueFD IPCUnixSocket::create()
{
	int sockets[2];
	int ret;

	ret = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
	if (ret) {
		ret = -errno;
		LOG(IPCUnixSocket, Error)
			<< "Failed to create socket pair: " << strerror(-ret);
		return {};
	}

	std::array<UniqueFD, 2> socketFds{
		UniqueFD(sockets[0]),
		UniqueFD(sockets[1]),
	};

	if (bind(std::move(socketFds[0])) < 0)
		return {};

	return std::move(socketFds[1]);
}

/**
 * \brief Bind to an existing IPC channel
 * \param[in] fd File descriptor
 *
 * This function binds the socket instance to an existing IPC channel identified
 * by the file descriptor \a fd. The file descriptor is obtained from the
 * IPCUnixSocket::create() function.
 *
 * \return 0 on success or a negative error code otherwise
 */
int IPCUnixSocket::bind(UniqueFD fd)
{
	if (isBound())
		return -EINVAL;

	fd_ = std::move(fd);
	notifier_ = new EventNotifier(fd_.get(), EventNotifier::Read);
	notifier_->activated.connect(this, &IPCUnixSocket::dataNotifier);

	return 0;
}

/**
 * \brief Close the IPC channel
 *
 * No communication is possible after close() has been called.
 */
void IPCUnixSocket::close()
{
	if (!isBound())
		return;

	delete notifier_;
	notifier_ = nullptr;

	fd_.reset();
	headerReceived_ = false;
}

/**
 * \brief Check if the IPC channel is bound
 * \return True if the IPC channel is bound, false otherwise
 */
bool IPCUnixSocket::isBound() const
{
	return fd_.isValid();
}

/**
 * \brief Send a message payload
 * \param[in] payload Message payload to send
 *
 * This function queues the message payload for transmission to the other end of
 * the IPC channel. It returns immediately, before the message is delivered to
 * the remote side.
 *
 * \return 0 on success or a negative error code otherwise
 */
int IPCUnixSocket::send(const Payload &payload)
{
	int ret;

	if (!isBound())
		return -ENOTCONN;

	Header hdr = {};
	hdr.data = payload.data.size();
	hdr.fds = payload.fds.size();

	if (!hdr.data && !hdr.fds)
		return -EINVAL;

	ret = ::send(fd_.get(), &hdr, sizeof(hdr), 0);
	if (ret < 0) {
		ret = -errno;
		LOG(IPCUnixSocket, Error)
			<< "Failed to send: " << strerror(-ret);
		return ret;
	}

	return sendData(payload.data.data(), hdr.data, payload.fds.data(), hdr.fds);
}

/**
 * \brief Receive a message payload
 * \param[out] payload Payload where to write the received message
 *
 * This function receives the message payload from the IPC channel and writes it