2023-02-22 23:04:39 +01:00

164 lines
4.5 KiB
C++

#ifdef USE_LIBCAMERA
#include "libcamera.hh"
#include <inttypes.h>
int libcamera_buffer_open(buffer_t *buf)
{
buf->libcamera = new buffer_libcamera_t{};
buf->libcamera->request = buf->buf_list->dev->libcamera->camera->createRequest(buf->index);
if (!buf->libcamera->request) {
LOG_INFO(buf, "Can't create request");
return -1;
}
auto &configurations = buf->buf_list->dev->libcamera->configuration;
auto &configuration = configurations->at(buf->buf_list->index);
auto stream = configuration.stream();
const std::vector<std::unique_ptr<libcamera::FrameBuffer>> &buffers =
buf->buf_list->dev->libcamera->allocator->buffers(stream);
auto const &buffer = buffers[buf->index];
if (buf->libcamera->request->addBuffer(stream, buffer.get()) < 0) {
LOG_ERROR(buf, "Can't set buffer for request");
}
if (buf->start) {
LOG_ERROR(buf, "Too many streams.");
}
if (buffer->planes().empty()) {
LOG_ERROR(buf, "No planes allocated");
}
{
uint64_t offset = buffer->planes()[0].offset;
uint64_t length = 0;
libcamera::SharedFD dma_fd = buffer->planes()[0].fd;
// Require that planes are continuous
for (auto const &plane : buffer->planes()) {
if (plane.fd != dma_fd) {
LOG_ERROR(buf, "Plane does not share FD: fd=%d, expected=%d", plane.fd.get(), dma_fd.get());
}
if (offset + length != plane.offset) {
LOG_ERROR(buf, "Plane is not continuous: offset=%u, expected=%" PRIu64, plane.offset, offset + length);
}
length += plane.length;
}
buf->start = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd.get(), offset);
if (!buf->start || buf->start == MAP_FAILED) {
LOG_ERROR(buf, "Failed to mmap DMA buffer");
}
buf->dma_fd = dma_fd.get();
buf->length = length;
LOG_DEBUG(buf, "Mapped buffer: start=%p, length=%zu, fd=%d, planes=%zu",
buf->start, buf->length, buf->dma_fd, buffer->planes().size());
}
return 0;
error:
return -1;
}
void libcamera_buffer_close(buffer_t *buf)
{
if (buf->libcamera) {
delete buf->libcamera;
buf->libcamera = NULL;
}
}
int libcamera_buffer_enqueue(buffer_t *buf, const char *who)
{
auto &request = buf->libcamera->request;
auto const &camera = buf->buf_list->dev->libcamera->camera;
request->reuse(libcamera::Request::ReuseBuffers);
request->controls() = std::move(buf->buf_list->dev->libcamera->controls);
if (camera->queueRequest(buf->libcamera->request.get()) < 0) {
LOG_ERROR(buf, "Can't queue buffer.");
}
buf->buf_list->dev->libcamera->controls.clear();
return 0;
error:
return -1;
}
void libcamera_buffer_dump_metadata(buffer_t *buf)
{
auto &metadata = buf->libcamera->request->metadata();
auto idMap = metadata.idMap();
for (auto const &control : metadata) {
if (!control.first)
continue;
auto control_id = control.first;
auto control_value = control.second;
std::string control_id_name = "";
if (auto control_id_info = idMap ? idMap->at(control_id) : NULL) {
control_id_name = control_id_info->name();
}
LOG_VERBOSE(buf, "Metadata: %s (%08x, type=%d): %s",
control_id_name.c_str(), control_id, control_value.type(), control_value.toString().c_str());
}
}
void buffer_list_libcamera_t::libcamera_buffer_list_dequeued(libcamera::Request *request)
{
if (request->status() == libcamera::Request::RequestComplete) {
unsigned index = request->cookie();
if (write(buf_list->libcamera->fds[1], &index, sizeof(index)) == sizeof(index)) {
return;
}
}
// put back into queue, as it failed
buf_list->dev->libcamera->camera->queueRequest(request);
}
int libcamera_buffer_list_dequeue(buffer_list_t *buf_list, buffer_t **bufp)
{
unsigned index = 0;
int n = read(buf_list->libcamera->fds[0], &index, sizeof(index));
if (n != sizeof(index)) {
LOG_INFO(buf_list, "Received invalid result from `read`: %d", n);
return -1;
}
if (index >= (unsigned)buf_list->nbufs) {
LOG_INFO(buf_list, "Received invalid index from `read`: %d >= %d", index, buf_list->nbufs);
return -1;
}
*bufp = buf_list->bufs[index];
if (index == 0) {
libcamera_buffer_dump_metadata(*bufp);
}
return 0;
}
int libcamera_buffer_list_pollfd(buffer_list_t *buf_list, struct pollfd *pollfd, bool can_dequeue)
{
int count_enqueued = buffer_list_count_enqueued(buf_list);
pollfd->fd = buf_list->libcamera->fds[0]; // write end
pollfd->events = POLLHUP;
if (can_dequeue && count_enqueued > 0) {
pollfd->events |= POLLIN;
}
pollfd->revents = 0;
return 0;
}
#endif // USE_LIBCAMERA