libcamera: implement allocation and dataflow

Keep track of the negotiated parameters and use those to construct
the Buffers param.

Use the FrameAllocator to allocate buffer memory. Keep a request for
each buffer and queue them.

In requestComplete, put the request metadata info into the buffer and
header and write the finished buffer id to a ringbuffer. Then wake up
the data thread to pop the finished buffer_id and push it in the
graph.

Remove some old files.
This commit is contained in:
Wim Taymans 2021-11-03 16:24:16 +01:00
parent b2c38a2b3b
commit 0fd5e3fdb2
4 changed files with 262 additions and 1361 deletions

View file

@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <deque>
#include <spa/support/plugin.h>
#include <spa/support/log.h>
@ -39,6 +40,7 @@
#include <spa/utils/names.h>
#include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/utils/ringbuffer.h>
#include <spa/monitor/device.h>
#include <spa/node/node.h>
#include <spa/node/io.h>
@ -52,6 +54,8 @@
#include <libcamera/camera.h>
#include <libcamera/stream.h>
#include <libcamera/formats.h>
#include <libcamera/framebuffer.h>
#include <libcamera/framebuffer_allocator.h>
#include "libcamera.h"
#include "libcamera-manager.hpp"
@ -66,7 +70,8 @@ static void reset_props(struct props *props)
spa_zero(*props);
}
#define MAX_BUFFERS 32
#define MAX_BUFFERS 32
#define MASK_BUFFERS 31
#define BUFFER_FLAG_OUTSTANDING (1<<0)
#define BUFFER_FLAG_ALLOCATED (1<<1)
@ -95,6 +100,7 @@ struct port {
bool have_format;
struct spa_video_info current_format;
struct spa_fraction rate;
StreamConfiguration streamConfig;
uint32_t memtype;
@ -104,8 +110,8 @@ struct port {
struct buffer buffers[MAX_BUFFERS];
uint32_t n_buffers;
struct spa_list queue;
struct spa_source source;
struct spa_ringbuffer ring;
uint32_t ring_ids[MAX_BUFFERS];
uint64_t info_all;
struct spa_port_info info;
@ -123,7 +129,6 @@ struct port {
struct impl {
struct spa_handle handle;
struct spa_node node;
bool have_source;
struct spa_log *log;
struct spa_loop *data_loop;
@ -145,13 +150,23 @@ struct impl {
CameraManager *manager;
std::shared_ptr<Camera> camera;
FrameBufferAllocator *allocator;
std::vector<std::unique_ptr<libcamera::Request>> requestPool;
std::deque<libcamera::Request *> pendingRequests;
void requestComplete(libcamera::Request *request);
unsigned int have_config;
std::unique_ptr<CameraConfiguration> config;
struct spa_source source;
unsigned int active:1;
unsigned int acquired:1;
};
typedef struct impl Impl;
#define CHECK_PORT(impl,direction,port_id) ((direction) == SPA_DIRECTION_OUTPUT && (port_id) == 0)
#define GET_OUT_PORT(impl,p) (&impl->out_ports[p])
@ -409,15 +424,12 @@ static int impl_node_remove_port(void *object,
return -ENOTSUP;
}
static int port_get_format(void *object,
enum spa_direction direction, uint32_t port_id,
static int port_get_format(struct impl *impl, struct port *port,
uint32_t index,
const struct spa_pod *filter,
struct spa_pod **param,
struct spa_pod_builder *builder)
{
struct impl *impl = (struct impl*)object;
struct port *port = GET_PORT(impl, direction, port_id);
struct spa_pod_frame f;
if (!port->have_format)
@ -492,14 +504,13 @@ next:
switch (id) {
case SPA_PARAM_PropInfo:
return spa_libcamera_enum_controls(impl, seq, start, num, filter);
return spa_libcamera_enum_controls(impl, port, seq, start, num, filter);
case SPA_PARAM_EnumFormat:
return spa_libcamera_enum_format(impl, seq, start, num, filter);
return spa_libcamera_enum_format(impl, port, seq, start, num, filter);
case SPA_PARAM_Format:
if((res = port_get_format(impl, direction, port_id,
result.index, filter, &param, &b)) <= 0)
if((res = port_get_format(impl, port, result.index, filter, &param, &b)) <= 0)
return res;
break;
case SPA_PARAM_Buffers:
@ -512,15 +523,14 @@ next:
/* Get the number of buffers to be used from libcamera and send the same to pipewire
* so that exact number of buffers are allocated
*/
// uint32_t n_buffers = libcamera_get_nbuffers(port->dev.camera);
uint32_t n_buffers = 0;
uint32_t n_buffers = port->streamConfig.bufferCount;
param = (struct spa_pod*)spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, id,
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(n_buffers, n_buffers, n_buffers),
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
SPA_PARAM_BUFFERS_size, SPA_POD_Int(0),
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(0),
SPA_PARAM_BUFFERS_size, SPA_POD_Int(port->streamConfig.frameSize),
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->streamConfig.stride),
SPA_PARAM_BUFFERS_align, SPA_POD_Int(16));
break;
}
@ -575,14 +585,10 @@ next:
return 0;
}
static int port_set_format(void *object,
enum spa_direction direction, uint32_t port_id,
uint32_t flags,
const struct spa_pod *format)
static int port_set_format(struct impl *impl, struct port *port,
uint32_t flags, const struct spa_pod *format)
{
struct impl *impl = (struct impl*)object;
struct spa_video_info info;
struct port *port = GET_PORT(impl, direction, port_id);
int res;
if (format == NULL) {
@ -590,7 +596,7 @@ static int port_set_format(void *object,
return 0;
spa_libcamera_stream_off(impl);
spa_libcamera_clear_buffers(impl);
spa_libcamera_clear_buffers(impl, port);
port->have_format = false;
spa_libcamera_close(impl);
@ -644,11 +650,11 @@ static int port_set_format(void *object,
}
if (port->have_format && !(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) {
spa_libcamera_use_buffers(impl, NULL, 0);
spa_libcamera_use_buffers(impl, port, NULL, 0);
port->have_format = false;
}
if (spa_libcamera_set_format(impl, &info, flags & SPA_NODE_PARAM_FLAG_TEST_ONLY) < 0)
if (spa_libcamera_set_format(impl, port, &info, flags & SPA_NODE_PARAM_FLAG_TEST_ONLY) < 0)
return -EINVAL;
if (!(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) {
@ -675,15 +681,23 @@ static int impl_node_port_set_param(void *object,
uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
struct impl *impl = (struct impl*)object;
struct port *port;
int res;
spa_return_val_if_fail(object != NULL, -EINVAL);
spa_return_val_if_fail(CHECK_PORT(impl, direction, port_id), -EINVAL);
spa_return_val_if_fail(CHECK_PORT(object, direction, port_id), -EINVAL);
port = GET_PORT(impl, direction, port_id);
if (id == SPA_PARAM_Format) {
return port_set_format(object, direction, port_id, flags, param);
switch (id) {
case SPA_PARAM_Format:
res = port_set_format(impl, port, flags, param);
break;
default:
res = -ENOENT;
}
else
return -ENOENT;
return res;
}
static int impl_node_port_use_buffers(void *object,
@ -707,16 +721,16 @@ static int impl_node_port_use_buffers(void *object,
if (port->n_buffers) {
spa_libcamera_stream_off(impl);
if ((res = spa_libcamera_clear_buffers(impl)) < 0)
if ((res = spa_libcamera_clear_buffers(impl, port)) < 0)
return res;
}
if (buffers == NULL)
return 0;
if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) {
res = spa_libcamera_alloc_buffers(impl, buffers, n_buffers);
res = spa_libcamera_alloc_buffers(impl, port, buffers, n_buffers);
} else {
res = spa_libcamera_use_buffers(impl, buffers, n_buffers);
res = spa_libcamera_use_buffers(impl, port, buffers, n_buffers);
}
return res;
}
@ -763,16 +777,14 @@ static int impl_node_port_reuse_buffer(void *object,
spa_return_val_if_fail(buffer_id < port->n_buffers, -EINVAL);
res = spa_libcamera_buffer_recycle(impl, buffer_id);
res = spa_libcamera_buffer_recycle(impl, port, buffer_id);
return res;
}
static void set_control(struct impl *impl, struct port *port, uint32_t control_id, float value)
{
// if(libcamera_set_control(port->dev.camera, control_id, value) < 0) {
spa_log_error(impl->log, "Failed to set control");
// }
spa_log_error(impl->log, "Failed to set control");
}
static int process_control(struct impl *impl, struct spa_pod_sequence *control)
@ -825,7 +837,7 @@ static int impl_node_process(void *object)
}
if (io->buffer_id < port->n_buffers) {
if ((res = spa_libcamera_buffer_recycle(impl, io->buffer_id)) < 0)
if ((res = spa_libcamera_buffer_recycle(impl, port, io->buffer_id)) < 0)
return res;
io->buffer_id = SPA_ID_INVALID;
@ -886,15 +898,9 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void
static int impl_clear(struct spa_handle *handle)
{
struct impl *impl;
struct port *port;
impl = (struct impl *) handle;
port = GET_OUT_PORT(impl, 0);
if(impl->have_source) {
spa_system_close(impl->system, port->source.fd);
impl->have_source = false;
}
impl->~Impl();
return 0;
}
@ -920,11 +926,11 @@ impl_init(const struct spa_handle_factory *factory,
spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(handle != NULL, -EINVAL);
impl = new(handle) Impl();
handle->get_interface = impl_get_interface;
handle->clear = impl_clear;
impl = (struct impl *) handle;
impl->log = (struct spa_log*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
libcamera_log_topic_init(impl->log);

View file

@ -40,6 +40,9 @@ int spa_libcamera_open(struct impl *impl)
if (impl->acquired)
return 0;
impl->camera->acquire();
impl->allocator = new FrameBufferAllocator(impl->camera);
impl->acquired = true;
return 0;
}
@ -51,7 +54,12 @@ int spa_libcamera_close(struct impl *impl)
return 0;
if (impl->active || port->have_format)
return 0;
delete impl->allocator;
impl->allocator = nullptr;
impl->camera->release();
impl->acquired = false;
return 0;
}
@ -67,21 +75,69 @@ static void spa_libcamera_get_config(struct impl *impl)
impl->have_config = true;
}
static int spa_libcamera_buffer_recycle(struct impl *impl, uint32_t buffer_id)
static int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, uint32_t buffer_id)
{
struct port *port = &impl->out_ports[0];
struct buffer *b = &port->buffers[buffer_id];
int res;
if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING))
return 0;
SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUTSTANDING);
if (buffer_id >= impl->requestPool.size()) {
spa_log_warn(impl->log, "invalid buffer_id %u >= %zu",
buffer_id, impl->requestPool.size());
return -EINVAL;
}
Request *request = impl->requestPool[buffer_id].get();
Stream *stream = port->streamConfig.stream();
FrameBuffer *buffer = impl->allocator->buffers(stream)[buffer_id].get();
if ((res = request->addBuffer(stream, buffer)) < 0) {
spa_log_warn(impl->log, "can't add buffer %u for request: %s",
buffer_id, spa_strerror(res));
return -ENOMEM;
}
if (!impl->active) {
impl->pendingRequests.push_back(request);
return 0;
} else {
if ((res = impl->camera->queueRequest(request)) < 0) {
spa_log_warn(impl->log, "can't queue buffer %u: %s",
buffer_id, spa_strerror(res));
return res == -EACCES ? -EBUSY : res;
}
}
return 0;
}
static int spa_libcamera_clear_buffers(struct impl *impl)
static int allocBuffers(struct impl *impl, struct port *port, unsigned int count)
{
int res;
if ((res = impl->allocator->allocate(port->streamConfig.stream())) < 0)
return res;
for (unsigned int i = 0; i < count; i++) {
std::unique_ptr<Request> request = impl->camera->createRequest(i);
if (!request) {
impl->requestPool.clear();
return -ENOMEM;
}
impl->requestPool.push_back(std::move(request));
}
return res;
}
static void freeBuffers(struct impl *impl, struct port *port)
{
impl->pendingRequests.clear();
impl->requestPool.clear();
impl->allocator->free(port->streamConfig.stream());
}
static int spa_libcamera_clear_buffers(struct impl *impl, struct port *port)
{
struct port *port = &impl->out_ports[0];
uint32_t i;
if (port->n_buffers == 0)
@ -96,7 +152,7 @@ static int spa_libcamera_clear_buffers(struct impl *impl)
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) {
spa_log_debug(impl->log, "queueing outstanding buffer %p", b);
spa_libcamera_buffer_recycle(impl, i);
spa_libcamera_buffer_recycle(impl, port, i);
}
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) {
munmap(SPA_PTROFF(b->ptr, -d[0].mapoffset, void),
@ -108,6 +164,7 @@ static int spa_libcamera_clear_buffers(struct impl *impl)
d[0].type = SPA_ID_INVALID;
}
freeBuffers(impl, port);
port->n_buffers = 0;
return 0;
@ -166,9 +223,7 @@ static const struct format_info *video_format_to_info(const PixelFormat &pix) {
}
static const struct format_info *find_format_info_by_media_type(uint32_t type,
uint32_t subtype,
uint32_t format,
int startidx)
uint32_t subtype, uint32_t format, int startidx)
{
size_t i;
@ -182,11 +237,9 @@ static const struct format_info *find_format_info_by_media_type(uint32_t type,
}
static int
spa_libcamera_enum_format(struct impl *impl, int seq,
uint32_t start, uint32_t num,
const struct spa_pod *filter)
spa_libcamera_enum_format(struct impl *impl, struct port *port, int seq,
uint32_t start, uint32_t num, const struct spa_pod *filter)
{
struct port *port = &impl->out_ports[0];
int res;
const struct format_info *info;
uint8_t buffer[1024];
@ -298,13 +351,14 @@ next_fmt:
return res;
}
static int spa_libcamera_set_format(struct impl *impl, struct spa_video_info *format, bool try_only)
static int spa_libcamera_set_format(struct impl *impl, struct port *port,
struct spa_video_info *format, bool try_only)
{
struct port *port = &impl->out_ports[0];
const struct format_info *info = NULL;
uint32_t video_format;
struct spa_rectangle *size = NULL;
struct spa_fraction *framerate = NULL;
CameraConfiguration::Status validation;
int res;
switch (format->media_subtype) {
@ -341,10 +395,15 @@ static int spa_libcamera_set_format(struct impl *impl, struct spa_video_info *fo
streamConfig.pixelFormat = info->pix;
streamConfig.size.width = size->width;
streamConfig.size.height = size->height;
streamConfig.bufferCount = 8;
if (impl->config->validate() == CameraConfiguration::Invalid)
validation = impl->config->validate();
if (validation == CameraConfiguration::Invalid)
return -EINVAL;
if (try_only)
return 0;
if ((res = spa_libcamera_open(impl)) < 0)
return res;
@ -352,6 +411,11 @@ static int spa_libcamera_set_format(struct impl *impl, struct spa_video_info *fo
if (res != 0)
goto error;
port->streamConfig = impl->config->at(0);
if ((res = allocBuffers(impl, port, port->streamConfig.bufferCount)) < 0)
return res;
port->have_format = true;
port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE;
@ -369,127 +433,26 @@ error:
}
static int
spa_libcamera_enum_controls(struct impl *impl, int seq,
spa_libcamera_enum_controls(struct impl *impl, struct port *port, int seq,
uint32_t start, uint32_t num,
const struct spa_pod *filter)
{
return -ENOTSUP;
}
static int mmap_read(struct impl *impl)
{
#if 0
struct port *port = &impl->out_ports[0];
struct buffer *b = NULL;
struct spa_data *d = NULL;
unsigned int sequence = 0;
struct timeval timestamp;
int64_t pts;
struct OutBuf *pOut = NULL;
struct CamData *pDatas = NULL;
uint32_t bytesused = 0;
timestamp.tv_sec = 0;
timestamp.tv_usec = 0;
if (impl->camera) {
// pOut = (struct OutBuf *)libcamera_get_ring_buffer_data(dev->camera);
if(!pOut) {
spa_log_debug(impl->log, "Exiting %s as pOut is NULL", __FUNCTION__);
return -1;
}
/* update the read index of the ring buffer */
// libcamera_ringbuffer_read_update(dev->camera);
pDatas = pOut->datas;
if(NULL == pDatas) {
spa_log_debug(impl->log, "Exiting %s on NULL pointer", __FUNCTION__);
goto end;
}
b = &port->buffers[pOut->bufIdx];
b->outbuf->n_datas = pOut->n_datas;
if(NULL == b->outbuf->datas) {
spa_log_debug(impl->log, "Exiting %s as b->outbuf->datas is NULL", __FUNCTION__);
goto end;
}
for(unsigned int i = 0; i < pOut->n_datas; ++i) {
struct CamData *pData = &pDatas[i];
if(NULL == pData) {
spa_log_debug(impl->log, "Exiting %s on NULL pointer", __FUNCTION__);
goto end;
}
b->outbuf->datas[i].flags = SPA_DATA_FLAG_READABLE;
if(port->memtype == SPA_DATA_DmaBuf) {
b->outbuf->datas[i].fd = pData->fd;
}
bytesused = b->outbuf->datas[i].chunk->size = pData->size;
timestamp = pData->timestamp;
sequence = pData->sequence;
b->outbuf->datas[i].mapoffset = 0;
b->outbuf->datas[i].chunk->offset = 0;
b->outbuf->datas[i].chunk->flags = 0;
//b->outbuf->datas[i].chunk->stride = pData->sstride; /* FIXME:: This needs to be appropriately filled */
b->outbuf->datas[i].maxsize = pData->maxsize;
spa_log_trace(impl->log,"Spa libcamera Source::%s:: got bufIdx = %d and ndatas = %d",
__FUNCTION__, pOut->bufIdx, pOut->n_datas);
spa_log_trace(impl->log," data[%d] --> fd = %ld bytesused = %d sequence = %d",
i, b->outbuf->datas[i].fd, bytesused, sequence);
}
}
pts = SPA_TIMEVAL_TO_NSEC(&timestamp);
if (impl->clock) {
impl->clock->nsec = pts;
impl->clock->rate = port->rate;
impl->clock->position = sequence;
impl->clock->duration = 1;
impl->clock->delay = 0;
impl->clock->rate_diff = 1.0;
impl->clock->next_nsec = pts + 1000000000LL / port->rate.denom;
}
if (b->h) {
b->h->flags = 0;
b->h->offset = 0;
b->h->seq = sequence;
b->h->pts = pts;
b->h->dts_offset = 0;
}
d = b->outbuf->datas;
d[0].chunk->offset = 0;
d[0].chunk->size = bytesused;
d[0].chunk->flags = 0;
d[0].data = b->ptr;
spa_log_trace(impl->log,"%s:: b->ptr = %p d[0].data = %p",
__FUNCTION__, b->ptr, d[0].data);
spa_list_append(&port->queue, &b->link);
end:
// libcamera_free_CamData(dev->camera, pDatas);
// libcamera_free_OutBuf(dev->camera, pOut);
#endif
return 0;
}
static void libcamera_on_fd_events(struct spa_source *source)
{
struct impl *impl = (struct impl*) source->data;
struct spa_io_buffers *io;
struct port *port = &impl->out_ports[0];
uint32_t index, buffer_id;
struct buffer *b;
uint64_t cnt;
if (source->rmask & SPA_IO_ERR) {
struct port *port = &impl->out_ports[0];
spa_log_error(impl->log, "libcamera %p: error %08x", impl, source->rmask);
if (port->source.loop)
spa_loop_remove_source(impl->data_loop, &port->source);
if (impl->source.loop)
spa_loop_remove_source(impl->data_loop, &impl->source);
return;
}
@ -498,25 +461,25 @@ static void libcamera_on_fd_events(struct spa_source *source)
return;
}
if (spa_system_eventfd_read(impl->system, port->source.fd, &cnt) < 0) {
if (spa_system_eventfd_read(impl->system, impl->source.fd, &cnt) < 0) {
spa_log_error(impl->log, "Failed to read on event fd");
return;
}
if (mmap_read(impl) < 0) {
spa_log_debug(impl->log, "%s:: mmap_read failure", __FUNCTION__);
if (spa_ringbuffer_get_read_index(&port->ring, &index) < 1) {
spa_log_error(impl->log, "nothing is queued");
return;
}
buffer_id = port->ring_ids[index & MASK_BUFFERS];
spa_ringbuffer_read_update(&port->ring, index + 1);
if (spa_list_is_empty(&port->queue)) {
spa_log_debug(impl->log, "Exiting %s as spa list is empty", __FUNCTION__);
return;
}
b = &port->buffers[buffer_id];
spa_list_append(&port->queue, &b->link);
io = port->io;
if (io != NULL && io->status != SPA_STATUS_HAVE_DATA) {
if (io->buffer_id < port->n_buffers)
spa_libcamera_buffer_recycle(impl, io->buffer_id);
spa_libcamera_buffer_recycle(impl, port, io->buffer_id);
b = spa_list_first(&port->queue, struct buffer, link);
spa_list_remove(&b->link);
@ -529,103 +492,28 @@ static void libcamera_on_fd_events(struct spa_source *source)
spa_node_call_ready(&impl->callbacks, SPA_STATUS_HAVE_DATA);
}
static int spa_libcamera_use_buffers(struct impl *impl, struct spa_buffer **buffers, uint32_t n_buffers)
static int spa_libcamera_use_buffers(struct impl *impl, struct port *port,
struct spa_buffer **buffers, uint32_t n_buffers)
{
#if 0
struct port *port = &impl->out_ports[0];
unsigned int i, j;
struct spa_data *d;
n_buffers = libcamera_get_nbuffers(port->dev.camera);
if (n_buffers > 0) {
d = buffers[0]->datas;
if (d[0].type == SPA_DATA_MemFd ||
(d[0].type == SPA_DATA_MemPtr && d[0].data != NULL)) {
port->memtype = SPA_DATA_MemPtr;
} else if (d[0].type == SPA_DATA_DmaBuf) {
port->memtype = SPA_DATA_DmaBuf;
} else {
spa_log_error(impl->log, "v4l2: can't use buffers of type %d", d[0].type);
return -EINVAL;
}
}
for (i = 0; i < n_buffers; i++) {
struct buffer *b;
b = &port->buffers[i];
b->id = i;
b->outbuf = buffers[i];
b->flags = BUFFER_FLAG_OUTSTANDING;
b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
spa_log_debug(impl->log, "import buffer %p", buffers[i]);
if (buffers[i]->n_datas < 1) {
spa_log_error(impl->log, "invalid memory on buffer %p", buffers[i]);
return -EINVAL;
}
d = buffers[i]->datas;
for(j = 0; j < buffers[i]->n_datas; ++j) {
d[j].mapoffset = 0;
d[j].maxsize = libcamera_get_max_size(port->dev.camera);
if (port->memtype == SPA_DATA_MemPtr) {
if (d[j].data == NULL) {
d[j].fd = -1;
d[j].data = mmap(NULL,
d[j].maxsize + d[j].mapoffset,
PROT_READ, MAP_SHARED,
libcamera_get_fd(port->dev.camera, i, j),
0);
if (d[j].data == MAP_FAILED) {
return -errno;
}
b->ptr = d[j].data;
spa_log_debug(impl->log, "In spa_libcamera_use_buffers(). mmap ptr:%p for fd = %ld buffer: #%d",
d[j].data, d[j].fd, i);
SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
} else {
b->ptr = d[j].data;
spa_log_debug(impl->log, "In spa_libcamera_use_buffers(). b->ptr = %p d[j].maxsize = %d for buffer: #%d",
d[j].data, d[j].maxsize, i);
}
spa_log_debug(impl->log, "In spa_libcamera_use_buffers(). setting b->ptr = %p for buffer: #%d on libcamera",
b->ptr, i);
}
else if (port->memtype == SPA_DATA_DmaBuf) {
d[j].fd = libcamera_get_fd(port->dev.camera, i, j);
spa_log_debug(impl->log, "Got fd = %ld for buffer: #%d", d[j].fd, i);
}
else {
spa_log_error(impl->log, "Exiting spa_libcamera_use_buffers() with -EIO");
return -EIO;
}
}
spa_libcamera_buffer_recycle(impl, i);
}
port->n_buffers = n_buffers;
#endif
return 0;
return -ENOTSUP;
}
static int
mmap_init(struct impl *impl,
mmap_init(struct impl *impl, struct port *port,
struct spa_buffer **buffers, uint32_t n_buffers)
{
#if 0
struct port *port = &impl->out_ports[0];
unsigned int i, j;
struct spa_data *d;
Stream *stream = impl->config->at(0).stream();
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
impl->allocator->buffers(stream);
spa_log_info(impl->log, "In mmap_init()");
if (n_buffers > 0) {
if (bufs.size() != n_buffers)
return -EINVAL;
d = buffers[0]->datas;
if (d[0].type != SPA_ID_INVALID &&
@ -642,10 +530,7 @@ mmap_init(struct impl *impl,
}
}
/* get n_buffers from libcamera */
uint32_t libcamera_nbuffers = libcamera_get_nbuffers(port->dev.camera);
for (i = 0; i < libcamera_nbuffers; i++) {
for (i = 0; i < n_buffers; i++) {
struct buffer *b;
if (buffers[i]->n_datas < 1) {
@ -657,22 +542,22 @@ mmap_init(struct impl *impl,
b->id = i;
b->outbuf = buffers[i];
b->flags = BUFFER_FLAG_OUTSTANDING;
b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
b->h = (struct spa_meta_header*)spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
d = buffers[i]->datas;
for(j = 0; j < buffers[i]->n_datas; ++j) {
d[j].type = port->memtype;
d[j].flags = SPA_DATA_FLAG_READABLE;
d[j].mapoffset = 0;
d[j].maxsize = libcamera_get_max_size(port->dev.camera);
d[j].maxsize = port->streamConfig.frameSize;
d[j].chunk->offset = 0;
d[j].chunk->size = 0;
d[j].chunk->stride = port->fmt.bytesperline; /* FIXME:: This needs to be appropriately filled */
d[j].chunk->size = port->streamConfig.frameSize;
d[j].chunk->stride = port->streamConfig.stride;
d[j].chunk->flags = 0;
if (port->memtype == SPA_DATA_DmaBuf ||
port->memtype == SPA_DATA_MemFd) {
d[j].fd = libcamera_get_fd(port->dev.camera, i, j);
d[j].fd = bufs[i]->planes()[j].fd.fd();
spa_log_info(impl->log, "Got fd = %ld for buffer: #%d", d[j].fd, i);
d[j].data = NULL;
SPA_FLAG_SET(b->flags, BUFFER_FLAG_ALLOCATED);
@ -680,10 +565,10 @@ mmap_init(struct impl *impl,
else if(port->memtype == SPA_DATA_MemPtr) {
d[j].fd = -1;
d[j].data = mmap(NULL,
d[j].maxsize + d[j].mapoffset,
PROT_READ, MAP_SHARED,
libcamera_get_fd(port->dev.camera, i, j),
0);
d[j].maxsize + d[j].mapoffset,
PROT_READ, MAP_SHARED,
bufs[i]->planes()[j].fd.fd(),
0);
if (d[j].data == MAP_FAILED) {
spa_log_error(impl->log, "mmap: %m");
continue;
@ -696,35 +581,89 @@ mmap_init(struct impl *impl,
return -EIO;
}
}
spa_libcamera_buffer_recycle(impl, i);
spa_libcamera_buffer_recycle(impl, port, i);
}
port->n_buffers = libcamera_nbuffers;
#endif
port->n_buffers = n_buffers;
spa_log_info(impl->log, "we have %d buffers", n_buffers);
return 0;
}
static int
spa_libcamera_alloc_buffers(struct impl *impl,
spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
struct spa_buffer **buffers,
uint32_t n_buffers)
{
int res;
struct port *port = &impl->out_ports[0];
spa_log_info(impl->log, ". %d", port->n_buffers);
if (port->n_buffers > 0)
return -EIO;
if ((res = mmap_init(impl, buffers, n_buffers)) < 0) {
return -EIO;
}
if ((res = mmap_init(impl, port, buffers, n_buffers)) < 0)
return res;
return 0;
}
void Impl::requestComplete(libcamera::Request *request)
{
struct impl *impl = this;
struct port *port = &impl->out_ports[0];
Stream *stream = port->streamConfig.stream();
uint32_t index, buffer_id;
struct buffer *b;
spa_log_info(impl->log, "request complete");
if ((request->status() == Request::RequestCancelled)) {
spa_log_debug(impl->log, "Request was cancelled");
return;
}
FrameBuffer *buffer = request->findBuffer(stream);
if (buffer == nullptr) {
spa_log_warn(impl->log, "unknown buffer");
return;
}
const FrameMetadata &fmd = buffer->metadata();
buffer_id = request->cookie();
b = &port->buffers[buffer_id];
if (impl->clock) {
impl->clock->nsec = fmd.timestamp;
impl->clock->rate = port->rate;
impl->clock->position = fmd.sequence;
impl->clock->duration = 1;
impl->clock->delay = 0;
impl->clock->rate_diff = 1.0;
impl->clock->next_nsec = fmd.timestamp;
}
if (b->h) {
b->h->flags = 0;
b->h->offset = 0;
b->h->seq = fmd.sequence;
b->h->pts = fmd.timestamp;
b->h->dts_offset = 0;
}
request->reuse();
spa_ringbuffer_get_write_index(&port->ring, &index);
port->ring_ids[index & MASK_BUFFERS] = buffer_id;
spa_ringbuffer_write_update(&port->ring, index + 1);
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
spa_log_error(impl->log, "Failed to write on event fd");
}
static int spa_libcamera_stream_on(struct impl *impl)
{
struct port *port = &impl->out_ports[0];
int res;
if (!port->have_format) {
spa_log_error(impl->log, "Exting %s with -EIO", __FUNCTION__);
@ -736,24 +675,27 @@ static int spa_libcamera_stream_on(struct impl *impl)
spa_log_info(impl->log, "connecting camera");
// libcamera_connect(dev->camera);
impl->camera->requestCompleted.connect(impl, &impl::requestComplete);
// libcamera_start_capture(dev->camera);
if ((res = impl->camera->start()) < 0)
return res == -EACCES ? -EBUSY : res;
port->source.func = libcamera_on_fd_events;
port->source.data = impl;
port->source.fd = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
port->source.mask = SPA_IO_IN | SPA_IO_ERR;
port->source.rmask = 0;
if (port->source.fd < 0) {
spa_log_error(impl->log, "Failed to create eventfd. Exting %s with -EIO", __FUNCTION__);
} else {
spa_loop_add_source(impl->data_loop, &port->source);
impl->have_source = true;
for (Request *req : impl->pendingRequests) {
if ((res = impl->camera->queueRequest(req)) < 0)
return res == -EACCES ? -EBUSY : res;
}
impl->pendingRequests.clear();
// libcamera_set_spa_system(dev->camera, impl->system);
// libcamera_set_eventfd(dev->camera, port->source.fd);
impl->source.func = libcamera_on_fd_events;
impl->source.data = impl;
impl->source.fd = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
impl->source.mask = SPA_IO_IN | SPA_IO_ERR;
impl->source.rmask = 0;
if (impl->source.fd < 0) {
spa_log_error(impl->log, "Failed to create eventfd: %s", spa_strerror(impl->source.fd));
return impl->source.fd;
}
spa_loop_add_source(impl->data_loop, &impl->source);
impl->active = true;
@ -767,28 +709,39 @@ static int do_remove_source(struct spa_loop *loop,
size_t size,
void *user_data)
{
struct port *port = (struct port *)user_data;
if (port->source.loop)
spa_loop_remove_source(loop, &port->source);
struct impl *impl = (struct impl *)user_data;
if (impl->source.loop)
spa_loop_remove_source(loop, &impl->source);
return 0;
}
static int spa_libcamera_stream_off(struct impl *impl)
{
struct port *port = &impl->out_ports[0];
int res;
if (!impl->active)
if (!impl->active) {
for (std::unique_ptr<Request> &req : impl->requestPool)
req->reuse();
return 0;
}
spa_log_info(impl->log, "stopping camera");
// libcamera_stop_capture(dev->camera);
impl->pendingRequests.clear();
if ((res = impl->camera->stop()) < 0)
return res == -EACCES ? -EBUSY : res;
spa_log_info(impl->log, "disconnecting camera");
// libcamera_disconnect(dev->camera);
impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete);
spa_loop_invoke(impl->data_loop, do_remove_source, 0, NULL, 0, true, port);
spa_loop_invoke(impl->data_loop, do_remove_source, 0, NULL, 0, true, impl);
if (impl->source.fd >= 0) {
spa_system_close(impl->system, impl->source.fd);
impl->source.fd = -1;
}
spa_list_init(&port->queue);
impl->active = false;

View file

@ -1,926 +0,0 @@
/* Spa libcamera support
*
* Copyright (C) 2020, Collabora Ltd.
* Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com>
*
* libcamera_wrapper.cpp
*
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <climits>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <sys/mman.h>
#include <unistd.h>
#include <drm_fourcc.h>
#include <spa/support/log.h>
#include <spa/support/system.h>
#include <spa/param/props.h>
#include <spa/param/video/raw.h>
#include <libcamera/camera.h>
#include <libcamera/camera_manager.h>
#include <libcamera/request.h>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/framebuffer.h>
#include <libcamera/property_ids.h>
#include <libcamera/controls.h>
#include <libcamera/control_ids.h>
#include <linux/videodev2.h>
using namespace libcamera;
using namespace controls;
#include "libcamera_wrapper.h"
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
#define DEFAULT_PIXEL_FMT DRM_FORMAT_YUYV
/* Compressed formats
*
* TODO: Should be removed when the format gets merged in the
* libdrm.*/
#ifndef DRM_FORMAT_MJPEG
# define DRM_FORMAT_MJPEG fourcc_code('M', 'J', 'P', 'G') /* Motion-JPEG */
#endif
extern "C" {
static const struct {
spa_video_format video_format;
unsigned int drm_fourcc;
} format_map[] = {
{ SPA_VIDEO_FORMAT_ENCODED, DRM_FORMAT_MJPEG },
{ SPA_VIDEO_FORMAT_RGB, DRM_FORMAT_BGR888 },
{ SPA_VIDEO_FORMAT_BGR, DRM_FORMAT_RGB888 },
{ SPA_VIDEO_FORMAT_ARGB, DRM_FORMAT_BGRA8888 },
{ SPA_VIDEO_FORMAT_NV12, DRM_FORMAT_NV12 },
{ SPA_VIDEO_FORMAT_NV21, DRM_FORMAT_NV21 },
{ SPA_VIDEO_FORMAT_NV16, DRM_FORMAT_NV16 },
{ SPA_VIDEO_FORMAT_NV61, DRM_FORMAT_NV61 },
{ SPA_VIDEO_FORMAT_NV24, DRM_FORMAT_NV24 },
{ SPA_VIDEO_FORMAT_UYVY, DRM_FORMAT_UYVY },
{ SPA_VIDEO_FORMAT_VYUY, DRM_FORMAT_VYUY },
{ SPA_VIDEO_FORMAT_YUY2, DRM_FORMAT_YUYV },
{ SPA_VIDEO_FORMAT_YVYU, DRM_FORMAT_YVYU },
/* \todo NV42 is used in libcamera but is not mapped in here yet. */
};
typedef struct ring_buf {
uint32_t read_index;
uint32_t write_index;
}ring_buf;
typedef struct LibCamera {
std::unique_ptr<CameraManager> cm_;
std::shared_ptr<Camera> cam_;
std::unique_ptr<CameraConfiguration> config_;
FrameBufferAllocator *allocator_;
std::map<Stream*, std::string> streamName_;
std::vector<std::unique_ptr<Request>> requests_;
uint32_t nbuffers_;
uint32_t nplanes_;
uint32_t bufIdx_;
int64_t **fd_;
uint32_t maxSize_;
uint32_t width_;
uint32_t height_;
uint32_t pixelFormat_;
uint32_t stride_;
struct ring_buf ringbuf_;
void *ringbuf_data_[MAX_NUM_BUFFERS] = {};
struct spa_log *log_;
struct spa_system *system_;
int eventfd_ = -1;
pthread_mutex_t lock;
/* Methods */
int32_t listProperties();
void requestComplete(Request *request);
void item_free_fn();
void ring_buffer_init();
void *ring_buffer_read();
void ring_buffer_write(void *p);
bool open();
void close();
int request_capture();
int start();
void stop();
void connect();
void disconnect();
bool set_config();
std::shared_ptr<Camera> get_camera();
std::string choose_camera();
/* Mutators */
void set_streamcfg_width(uint32_t w);
void set_streamcfg_height(uint32_t h);
void set_streamcfgpixel_format(uint32_t fmt);
void set_max_size(uint32_t s);
void set_nbuffers(uint32_t n);
void set_nplanes(uint32_t n);
void set_stride(uint32_t s);
void set_fd(Stream *stream);
void ring_buffer_set_read_index(uint32_t idx);
void ring_buffer_set_write_index(uint32_t idx);
void ring_buffer_update_read_index();
void ring_buffer_update_write_index();
void reset_ring_buffer_data();
int32_t set_control(ControlList &controls, uint32_t control_id, float value);
/* Accessors */
uint32_t get_streamcfg_width();
uint32_t get_streamcfg_height();
uint32_t get_streamcfgpixel_format();
uint32_t get_max_size();
uint32_t get_nbuffers();
uint32_t get_nplanes();
uint32_t get_stride();
uint32_t ring_buffer_get_read_index();
uint32_t ring_buffer_get_write_index();
}LibCamera;
uint32_t LibCamera::get_max_size() {
return this->maxSize_;
}
void LibCamera::set_max_size(uint32_t s) {
this->maxSize_ = s;
}
uint32_t LibCamera::get_nbuffers() {
return this->nbuffers_;
}
void LibCamera::set_nbuffers(uint32_t n) {
this->nbuffers_ = n;
}
void LibCamera::set_nplanes(uint32_t n) {
this->nplanes_ = n;
}
void LibCamera::set_stride(uint32_t s) {
this->stride_ = s;
}
uint32_t LibCamera::get_stride() {
return this->stride_;
}
void LibCamera::set_fd(Stream *stream) {
this->fd_ = new int64_t*[this->nbuffers_];
uint32_t bufIdx = 0;
for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) {
uint32_t nplanes = buffer->planes().size();
this->fd_[bufIdx] = new int64_t[this->nplanes_];
for(uint32_t planeIdx = 0; planeIdx < nplanes; ++planeIdx) {
const FrameBuffer::Plane &plane = buffer->planes().front();
this->fd_[bufIdx][planeIdx] = plane.fd.fd();
}
bufIdx++;
}
}
uint32_t LibCamera::get_nplanes() {
return this->nplanes_;
}
void LibCamera::ring_buffer_init() {
this->ringbuf_.read_index = 0;
this->ringbuf_.write_index = 0;
}
uint32_t LibCamera::ring_buffer_get_read_index() {
uint32_t idx;
idx = __atomic_load_n(&this->ringbuf_.read_index, __ATOMIC_RELAXED);
return idx;
}
uint32_t LibCamera::ring_buffer_get_write_index() {
uint32_t idx;
idx = __atomic_load_n(&this->ringbuf_.write_index, __ATOMIC_RELAXED);
return idx;
}
void LibCamera::ring_buffer_set_read_index(uint32_t idx) {
__atomic_store_n(&this->ringbuf_.read_index, idx, __ATOMIC_RELEASE);
}
void LibCamera::ring_buffer_set_write_index(uint32_t idx) {
__atomic_store_n(&this->ringbuf_.write_index, idx, __ATOMIC_RELEASE);
}
void LibCamera::ring_buffer_update_read_index() {
uint32_t idx;
idx = this->ring_buffer_get_read_index();
this->ringbuf_data_[idx] = nullptr;
++idx;
if(idx == MAX_NUM_BUFFERS) {
idx = 0;
}
this->ring_buffer_set_read_index(idx);
}
void LibCamera::ring_buffer_update_write_index() {
uint32_t idx;
idx = this->ring_buffer_get_write_index();
++idx;
if(idx == MAX_NUM_BUFFERS) {
idx = 0;
}
this->ring_buffer_set_write_index(idx);
}
void LibCamera::ring_buffer_write(void *p)
{
uint32_t idx;
idx = this->ring_buffer_get_write_index();
pthread_mutex_lock(&this->lock);
ringbuf_data_[idx] = p;
pthread_mutex_unlock(&this->lock);
}
void *LibCamera::ring_buffer_read()
{
uint32_t idx;
void *p;
idx = this->ring_buffer_get_read_index();
pthread_mutex_lock(&this->lock);
p = (void *)this->ringbuf_data_[idx];
pthread_mutex_unlock(&this->lock);
return p;
}
void LibCamera::item_free_fn() {
uint32_t ringbuf_read_index;
struct OutBuf *pOut = NULL;
struct CamData *pDatas = NULL;
ringbuf_read_index = this->ring_buffer_get_read_index();
for(int i = 0; i < MAX_NUM_BUFFERS; i++) {
pOut = (struct OutBuf *)ringbuf_data_[ringbuf_read_index];
if(pOut) {
pDatas = pOut->datas;
if(pDatas) {
libcamera_free_CamData(this, pDatas);
}
libcamera_free_OutBuf(this, pOut);
}
++ringbuf_read_index;
if(ringbuf_read_index == MAX_NUM_BUFFERS) {
ringbuf_read_index = 0;
}
}
}
std::string LibCamera::choose_camera() {
if (!this->cm_) {
return std::string();
}
if (this->cm_->cameras().empty()) {
return std::string();
}
/* If only one camera is available, use it automatically. */
else if (this->cm_->cameras().size() == 1) {
return this->cm_->cameras()[0]->id();
}
/* TODO::
* 1. Allow the user to provide a camera name to select. *
* 2. Select the camera based on the camera name provided by User *
*/
/* For time being, return the first camera if more than 1 camera devices are available */
else {
return this->cm_->cameras()[0]->id();
}
}
std::shared_ptr<Camera> LibCamera::get_camera() {
std::string camName = this->choose_camera();
std::shared_ptr<Camera> cam;
if (camName == "") {
return nullptr;
}
cam = this->cm_->get(camName);
if (!cam) {
return nullptr;
}
/* Sanity check that the camera has streams. */
if (cam->streams().empty()) {
return nullptr;
}
return cam;
}
uint32_t LibCamera::get_streamcfg_width() {
return this->width_;
}
uint32_t LibCamera::get_streamcfg_height() {
return this->height_;
}
uint32_t LibCamera::get_streamcfgpixel_format() {
return this->pixelFormat_;
}
void LibCamera::set_streamcfg_width(uint32_t w) {
this->width_ = w;
}
void LibCamera::set_streamcfg_height(uint32_t h) {
this->height_ = h;
}
void LibCamera::set_streamcfgpixel_format(uint32_t fmt) {
this->pixelFormat_ = fmt;
}
bool LibCamera::set_config() {
if(!this->cam_) {
return false;
}
this->config_ = this->cam_->generateConfiguration({ StreamRole::VideoRecording });
if (!this->config_ || this->config_->size() != 1) {
return false;
}
StreamConfiguration &cfg = this->config_->at(0);
cfg.size.width = this->get_streamcfg_width();
cfg.size.height = this->get_streamcfg_height();
cfg.pixelFormat = PixelFormat(this->get_streamcfgpixel_format());
/* Validate the configuration. */
if (this->config_->validate() == CameraConfiguration::Invalid) {
return false;
}
if (this->cam_->configure(this->config_.get())) {
return false;
}
this->listProperties();
this->allocator_ = new FrameBufferAllocator(this->cam_);
uint32_t nbuffers = UINT_MAX, nplanes = 0;
Stream *stream = cfg.stream();
int ret = this->allocator_->allocate(stream);
if (ret < 0) {
return -ENOMEM;
}
uint32_t allocated = this->allocator_->buffers(cfg.stream()).size();
nbuffers = std::min(nbuffers, allocated);
this->set_nbuffers(nbuffers);
int id = 0;
uint32_t max_size = 0;
for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) {
nplanes = buffer->planes().size();
const FrameBuffer::Plane &plane = buffer->planes().front();
max_size = std::max(max_size, plane.length);
++id;
}
this->set_max_size(max_size);
this->set_nplanes(nplanes);
this->set_fd(stream);
this->set_stride(cfg.stride);
return true;
}
int LibCamera::request_capture() {
int ret = 0;
StreamConfiguration &cfg = this->config_->at(0);
Stream *stream = cfg.stream();
for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) {
std::unique_ptr<Request> request = this->cam_->createRequest();
if (!request) {
spa_log_error(this->log_, "Cannot create request");
return -ENOMEM;
}
if (request->addBuffer(stream, buffer.get())) {
spa_log_error(this->log_, "Failed to associating buffer with request");
return -ENOMEM;
}
this->requests_.push_back(std::move(request));
}
return ret;
}
bool LibCamera::open() {
std::shared_ptr<Camera> cam;
int ret = 0;
cam = this->get_camera();
if(!cam) {
return false;
}
ret = cam->acquire();
if (ret) {
return false;
}
this->cam_ = cam;
if(!this->set_config()) {
return false;
}
return true;
}
int LibCamera::start() {
for (unsigned int index = 0; index < this->config_->size(); ++index) {
StreamConfiguration &cfg = this->config_->at(index);
this->streamName_[cfg.stream()] = "stream" + std::to_string(index);
}
if(this->request_capture()) {
spa_log_error(this->log_, "failed to create request");
return -1;
}
spa_log_info(this->log_, "Starting camera ...");
/* start the camera now */
if (this->cam_->start()) {
spa_log_error(this->log_, "failed to start camera");
return -1;
}
this->ring_buffer_init();
for (std::unique_ptr<Request> &request : this->requests_) {
int ret = this->cam_->queueRequest(request.get());
if (ret < 0) {
spa_log_error(this->log_, "Cannot enqueue request");
return ret;
}
}
return 0;
}
void LibCamera::stop() {
StreamConfiguration &cfg = this->config_->at(0);
Stream *stream = cfg.stream();
uint32_t nbuffers = this->allocator_->buffers(stream).size();
for (uint32_t bufIdx = 0; bufIdx < nbuffers; bufIdx++) {
delete [] this->fd_[bufIdx];
}
delete [] this->fd_;
spa_log_info(this->log_, "Stopping camera ...");
this->cam_->stop();
if(this->allocator_) {
this->allocator_->free(stream);
delete this->allocator_;
this->allocator_ = nullptr;
}
this->item_free_fn();
this->requests_.clear();
this->streamName_.clear();
}
void LibCamera::close() {
if (this->cam_)
this->cam_->release();
}
void LibCamera::connect()
{
this->cam_->requestCompleted.connect(this, &LibCamera::requestComplete);
}
void LibCamera::disconnect()
{
this->cam_->requestCompleted.disconnect(this, &LibCamera::requestComplete);
}
uint32_t libcamera_get_streamcfg_width(LibCamera *camera) {
return camera->get_streamcfg_width();
}
uint32_t libcamera_get_streamcfg_height(LibCamera *camera) {
return camera->get_streamcfg_height();
}
uint32_t libcamera_get_streamcfgpixel_format(LibCamera *camera) {
return camera->get_streamcfgpixel_format();
}
void libcamera_set_streamcfg_width(LibCamera *camera, uint32_t w) {
camera->set_streamcfg_width(w);
}
void libcamera_set_streamcfg_height(LibCamera *camera, uint32_t h) {
camera->set_streamcfg_height(h);
}
void libcamera_set_streamcfgpixel_format(LibCamera *camera, uint32_t fmt) {
camera->set_streamcfgpixel_format(fmt);
}
bool libcamera_set_config(LibCamera *camera) {
return camera->set_config();
}
void libcamera_ringbuffer_read_update(LibCamera *camera) {
camera->ring_buffer_update_read_index();
}
void *libcamera_get_ring_buffer_data(LibCamera *camera) {
return camera->ring_buffer_read();
}
void libcamera_free_OutBuf(LibCamera *camera, OutBuf *p) {
pthread_mutex_lock(&camera->lock);
if(p != nullptr) {
delete p;
p = nullptr;
}
pthread_mutex_unlock(&camera->lock);
}
void libcamera_free_CamData(LibCamera *camera, CamData *p) {
pthread_mutex_lock(&camera->lock);
if(p != nullptr) {
delete p;
p = nullptr;
}
pthread_mutex_unlock(&camera->lock);
}
void libcamera_set_log(LibCamera *camera, struct spa_log *log) {
camera->log_ = log;
}
void libcamera_set_spa_system(LibCamera *camera, struct spa_system *system) {
camera->system_ = system;
}
void libcamera_set_eventfd(LibCamera *camera, int fd) {
camera->eventfd_ = fd;
}
spa_video_format libcamera_map_drm_fourcc_format(unsigned int fourcc) {
for (const auto &item : format_map) {
if (item.drm_fourcc == fourcc) {
return item.video_format;
}
}
return (spa_video_format)UINT32_MAX;
}
uint32_t libcamera_drm_to_video_format(unsigned int drm) {
return libcamera_map_drm_fourcc_format(drm);
}
uint32_t libcamera_video_format_to_drm(uint32_t format)
{
if (format == SPA_VIDEO_FORMAT_ENCODED) {
return DRM_FORMAT_INVALID;
}
for (const auto &item : format_map) {
if (item.video_format == format) {
return item.drm_fourcc;
}
}
return DRM_FORMAT_INVALID;
}
uint32_t libcamera_enum_streamcfgpixel_format(LibCamera *camera, uint32_t idx) {
if(!camera) {
return -1;
}
if (!camera->config_) {
spa_log_error(camera->log_, "Cannot get stream information without a camera");
return -EINVAL;
}
for (const StreamConfiguration &cfg : *camera->config_) {
uint32_t index = 0;
const StreamFormats &formats = cfg.formats();
for (PixelFormat pixelformat : formats.pixelformats()) {
if(index == idx) {
return pixelformat.fourcc();
}
++index;
}
}
/* We shouldn't be here */
return UINT32_MAX;
}
void libcamera_get_streamcfg_size(LibCamera *camera, uint32_t idx, uint32_t *width, uint32_t *height) {
if(!camera) {
return;
}
if (!camera->config_) {
spa_log_error(camera->log_, "Cannot get stream information without a camera");;
return;
}
for (const StreamConfiguration &cfg : *camera->config_) {
const StreamFormats &formats = cfg.formats();
for (PixelFormat pixelformat : formats.pixelformats()) {
uint32_t index = 0;
for (const Size &size : formats.sizes(pixelformat)) {
if(index == idx) {
*width = size.width;
*height = size.height;
return;
}
++index;
}
}
}
/* We shouldn't be here */
*width = *height = UINT32_MAX;
}
int LibCamera::listProperties()
{
if (!cam_) {
spa_log_error(log_, "Cannot list properties without a camera");;
return -EINVAL;
}
spa_log_info(log_, "listing properties");
for (const auto &prop : cam_->properties()) {
const ControlId *id = properties::properties.at(prop.first);
const ControlValue &value = prop.second;
spa_log_info(log_, "Property: %s = %s",id->name().c_str(), value.toString().c_str());
}
return 0;
}
int64_t libcamera_get_fd(LibCamera *camera, int bufIdx, int planeIdx) {
if((bufIdx >= (int)camera->nbuffers_) || (planeIdx >= (int)camera->nplanes_)){
return -1;
} else {
return camera->fd_[bufIdx][planeIdx];
}
}
int libcamera_get_max_size(LibCamera *camera) {
return camera->get_max_size();
}
void libcamera_connect(LibCamera *camera) {
if(!camera || !camera->cam_) {
return;
}
camera->connect();
}
uint32_t libcamera_get_nbuffers(LibCamera *camera) {
return camera->get_nbuffers();
}
uint32_t libcamera_get_nplanes(LibCamera *camera) {
return camera->get_nplanes();
}
uint32_t libcamera_get_stride(LibCamera *camera) {
return camera->get_stride();
}
int libcamera_start_capture(LibCamera *camera) {
if (!camera || !camera->cm_ || !camera->cam_) {
return -1;
}
return camera->start();
}
void libcamera_disconnect(LibCamera *camera) {
if(!camera || !camera->cam_) {
return;
}
camera->disconnect();
}
void libcamera_stop_capture(LibCamera *camera) {
if(!camera || !camera->cm_ || !camera->cam_) {
return;
}
camera->stop();
}
LibCamera* newLibCamera() {
int ret = 0;
pthread_mutexattr_t attr;
std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>();
LibCamera* camera = new LibCamera();
pthread_mutexattr_init(&attr);
pthread_mutex_init(&camera->lock, &attr);
ret = cm->start();
if (ret) {
deleteLibCamera(camera);
return nullptr;
}
camera->cm_ = std::move(cm);
camera->bufIdx_ = 0;
camera->set_streamcfg_width(DEFAULT_WIDTH);
camera->set_streamcfg_height(DEFAULT_HEIGHT);
camera->set_streamcfgpixel_format(DEFAULT_PIXEL_FMT);
if(!camera->open()) {
deleteLibCamera(camera);
return nullptr;
}
camera->ring_buffer_init();
return camera;
}
void deleteLibCamera(LibCamera *camera) {
if(camera == nullptr) {
return;
}
pthread_mutex_destroy(&camera->lock);
camera->close();
if(camera->cm_)
camera->cm_->stop();
delete camera;
camera = nullptr;
}
void LibCamera::requestComplete(Request *request) {
if (request->status() == Request::RequestCancelled) {
return;
}
++bufIdx_;
if(bufIdx_ >= nbuffers_) {
bufIdx_ = 0;
}
const Request::BufferMap &buffers = request->buffers();
for (auto it = buffers.begin(); it != buffers.end(); ++it) {
FrameBuffer *buffer = it->second;
unsigned int nplanes = buffer->planes().size();
OutBuf *pBuf = new OutBuf();
pBuf->bufIdx = bufIdx_;
pBuf->n_datas = nplanes;
pBuf->datas = new CamData[pBuf->n_datas];
unsigned int planeIdx = 0;
const std::vector<FrameBuffer::Plane> &planes = buffer->planes();
const FrameMetadata &metadata = buffer->metadata();
for (const FrameMetadata::Plane &plane : metadata.planes()) {
pBuf->datas[planeIdx].idx = planeIdx;
pBuf->datas[planeIdx].type = 3; /*SPA_DATA_DmaBuf;*/
pBuf->datas[planeIdx].fd = planes[planeIdx].fd.fd();
pBuf->datas[planeIdx].size = plane.bytesused;
pBuf->datas[planeIdx].maxsize = buffer->planes()[planeIdx].length;
pBuf->datas[planeIdx].sequence = metadata.sequence;
pBuf->datas[planeIdx].timestamp.tv_sec = metadata.timestamp / 1000000000;
pBuf->datas[planeIdx].timestamp.tv_usec = (metadata.timestamp / 1000) % 1000000;
++planeIdx;
}
/* Push the buffer to ring buffer */
if(pBuf && pBuf->datas) {
this->ring_buffer_write(pBuf);
/* Now update the write index of the ring buffer */
this->ring_buffer_update_write_index();
if(this->system_ && (this->eventfd_ > 0)) {
if (spa_system_eventfd_write(this->system_, this->eventfd_, 1) < 0) {
spa_log_error(log_, "Failed to write on event fd");
}
}
}
}
/*
* Create a new request and populate it with one buffer for each
* stream.
*/
for (auto it = buffers.begin(); it != buffers.end(); ++it) {
const Stream *stream = it->first;
FrameBuffer *buffer = it->second;
request->reuse();
request->addBuffer(stream, buffer);
cam_->queueRequest(request);
}
}
int32_t LibCamera::set_control(ControlList &controls, uint32_t control_id, float value) {
switch(control_id) {
case SPA_PROP_brightness:
controls.set(controls::Brightness, value);
break;
case SPA_PROP_contrast:
controls.set(controls::Contrast, value);
break;
case SPA_PROP_saturation:
controls.set(controls::Saturation, value);
break;
case SPA_PROP_exposure:
controls.set(controls::ExposureValue, value);
break;
case SPA_PROP_gain:
controls.set(controls::AnalogueGain, value);
break;
default:
return -1;
}
return 0;
}
int32_t libcamera_set_control(LibCamera *camera, uint32_t control_id, float value) {
int32_t res;
if(!camera || !camera->cm_ || !camera->cam_)
return -1;
std::unique_ptr<Request> request = camera->cam_->createRequest();
ControlList &controls = request->controls();
res = camera->set_control(controls, control_id, value);
camera->cam_->queueRequest(request.get());
return res;
}
}

View file

@ -1,132 +0,0 @@
/* Spa libcamera support
*
* Copyright (C) 2020, Collabora Ltd.
* Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com>
*
* libcamera_wrapper.h
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBCAMERA_WRAPPER_H
#define __LIBCAMERA_WRAPPER_H
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_NUM_BUFFERS 16
typedef struct CamData {
uint32_t idx;
uint32_t type;
int64_t fd;
uint32_t maxsize; /**< max size of data */
uint32_t size; /**< size of valid data. Should be clamped to
* maxsize. */
struct timeval timestamp;
uint32_t sequence;
void *data;
}CamData;
typedef struct OutBuf {
uint32_t bufIdx;
uint32_t n_datas; /**< number of data members */
struct CamData *datas; /**< array of data members */
}OutBuf;
typedef struct LibCamera LibCamera;
LibCamera *newLibCamera();
void deleteLibCamera(LibCamera *camera);
void libcamera_set_log(LibCamera *camera, struct spa_log *log);
bool libcamera_open(LibCamera *camera);
void libcamera_close(LibCamera *camera);
void libcamera_connect(LibCamera *camera);
void libcamera_disconnect(LibCamera *camera);
int libcamera_isCapturing(LibCamera *camera);
int libcamera_start_capture(LibCamera *camera);
void libcamera_stop_capture(LibCamera *camera);
int libcamera_get_refcnt(LibCamera *camera);
uint32_t libcamera_get_streamcfg_width(LibCamera *camera);
uint32_t libcamera_get_streamcfg_height(LibCamera *camera);
uint32_t libcamera_get_streamcfgpixel_format(LibCamera *camera);
uint32_t libcamera_enum_streamcfgpixel_format(LibCamera *camera, uint32_t idx);
uint32_t libcamera_video_format_to_drm(uint32_t fmt);
uint32_t libcamera_drm_to_video_format(unsigned int drm);
uint32_t libcamera_get_nbuffers(LibCamera *camera);
uint32_t libcamera_get_nplanes(LibCamera *camera);
int64_t libcamera_get_fd(LibCamera *camera, int bufIdx, int planeIdx);
int32_t libcamera_get_max_size(LibCamera *camera);
int32_t libcamera_set_control(LibCamera *camera, uint32_t control_id, float value);
void libcamera_set_streamcfg_width(LibCamera *camera, uint32_t w);
void libcamera_set_streamcfg_height(LibCamera *camera, uint32_t w);
void libcamera_set_streamcfgpixel_format(LibCamera *camera, uint32_t fmt);
bool libcamera_set_config(LibCamera *camera);
void libcamera_get_streamcfg_size(LibCamera *camera, uint32_t idx, uint32_t *width, uint32_t *height);
uint32_t libcamera_get_stride(LibCamera *camera);
void *libcamera_get_ring_buffer_data(LibCamera *camera);
void libcamera_reset_ring_buffer_data(LibCamera *camera);
void libcamera_ringbuffer_read_update(LibCamera *camera);
void libcamera_consume_data(LibCamera *camera);
void libcamera_free_CamData(LibCamera *camera, CamData *p);
void libcamera_free_OutBuf(LibCamera *camera, OutBuf *p);
void libcamera_set_spa_system(LibCamera *camera, struct spa_system *system);
void libcamera_set_eventfd(LibCamera *camera, int fd);
#ifdef __cplusplus
}
#endif /* extern "C" */
#endif /* __LIBCAMERA_WRAPPER_H */