diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 26 | ||||
-rw-r--r-- | lib/xlnx-buffers.c | 80 | ||||
-rw-r--r-- | lib/xlnx-buffers.h | 83 | ||||
-rw-r--r-- | lib/xlnx-events.c | 269 | ||||
-rw-r--r-- | lib/xlnx-plugin.c | 220 | ||||
-rw-r--r-- | lib/xlnx-priv.h | 107 | ||||
-rw-r--r-- | lib/xlnx-video.c | 388 |
7 files changed, 1173 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..cb91e0e --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,26 @@ +AR := $(CROSS_COMPILE)ar +CC := $(CROSS_COMPILE)gcc +CFLAGS := -O2 -W -Wall -Wno-unused-parameter -I$(KDIR)/usr/include -I$(topdir)/3rdparty/media-ctl/include -I$(topdir)/3rdparty/media-enum/include -I$(topdir)/include -I. -fPIC +LDFLAGS := -L$(topdir)/3rdparty/media-ctl -L$(topdir)/3rdparty/media-enum +LIBS := -lmediaenum -lmediactl -ldl + +OBJECTS := xlnx-buffers.o \ + xlnx-events.o \ + xlnx-plugin.o \ + xlnx-video.o + +TARGET := libxlnxvideo.so + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CC) -o $@ -shared $^ $(LDFLAGS) $(LIBS) + +%.o : %.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + -$(RM) *.o + -$(RM) $(TARGET) + +install: diff --git a/lib/xlnx-buffers.c b/lib/xlnx-buffers.c new file mode 100644 index 0000000..34f873a --- /dev/null +++ b/lib/xlnx-buffers.c @@ -0,0 +1,80 @@ +/* + * Xilinx Video Library - Buffers Pool + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <stdlib.h> +#include <string.h> + +#include "xlnx-buffers.h" + +struct xlnx_video_buffers_pool *xlnx_video_buffers_pool_new(unsigned int nbufs) +{ + struct xlnx_video_buffer *buffers; + struct xlnx_video_buffers_pool *pool; + unsigned int i; + + pool = malloc(sizeof *pool); + if (pool == NULL) + return NULL; + + buffers = malloc(sizeof *buffers * nbufs); + if (buffers == NULL) { + free(pool); + return NULL; + } + + memset(buffers, 0, sizeof *buffers * nbufs); + + for (i = 0; i < nbufs; ++i) + buffers[i].index = i; + + pool->nbufs = nbufs; + pool->buffers = buffers; + + return pool; +} + +void xlnx_video_buffers_pool_delete(struct xlnx_video_buffers_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->nbufs; ++i) { + if (pool->buffers[i].allocated) + free(pool->buffers[i].mem); + } + + free(pool->buffers); + free(pool); +} + +int xlnx_video_buffers_pool_alloc(struct xlnx_video_buffers_pool *pool, + size_t size, size_t align) +{ + unsigned int i; + int ret; + + for (i = 0; i < pool->nbufs; ++i) { + ret = posix_memalign(&pool->buffers[i].mem, align, size); + if (ret != 0) + return -ret; + + pool->buffers[i].size = size; + pool->buffers[i].allocated = true; + } + + return 0; +} diff --git a/lib/xlnx-buffers.h b/lib/xlnx-buffers.h new file mode 100644 index 0000000..2170150 --- /dev/null +++ b/lib/xlnx-buffers.h @@ -0,0 +1,83 @@ +/* + * Xilinx Video Library - Buffers Pool + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef __XLNX_VIDEO_BUFFERS_POOL_H__ +#define __XLNX_VIDEO_BUFFERS_POOL_H__ + +#include <stdbool.h> +#include <time.h> + +/* + * struct xlnx_video_buffer - Video buffer information + * @index: Zero-based buffer index, limited to the number of buffers minus one + * @size: Size of the video memory, in bytes + * @bytesused: Number of bytes used by video data, smaller or equal to @size + * @timestamp: Time stamp at which the buffer has been captured + * @error: True if an error occured while capturing video data for the buffer + * @allocated: True if memory for the buffer has been allocated by the pool + * @mem: Video data memory + */ +struct xlnx_video_buffer +{ + unsigned int index; + unsigned int size; + unsigned int bytesused; + struct timeval timestamp; + bool error; + bool allocated; + void *mem; +}; + +struct xlnx_video_buffers_pool +{ + unsigned int nbufs; + struct xlnx_video_buffer *buffers; +}; + +/* + * xlnx_video_buffers_pool_new - Create a new buffers pool + * @nbufs: Number of buffers in the pool + * + * Allocate a new buffers pool with space for @nbufs buffers. Memory for the + * buffers is not allocated. + */ +struct xlnx_video_buffers_pool *xlnx_video_buffers_pool_new(unsigned int nbufs); + +/* + * xlnx_video_buffers_pool_delete - Delete a buffers pool + * @pool: Buffers pool + * + * Delete a buffers pool and free buffers memory if it has been allocated by + * the pool. + */ +void xlnx_video_buffers_pool_delete(struct xlnx_video_buffers_pool *pool); + +/* + * xlnx_video_buffers_pool_alloc - Allocate memory for buffers in a pool + * @pool: Buffers pool + * @size: Buffer size + * @align: Buffer memory alignment in bytes + * + * Allocate @size bytes of memory for each buffer aligned to a multiple of + * @align bytes. The alignment must be a power of 2. + * + * Return 0 on success or a negative error code on failure. + */ +int xlnx_video_buffers_pool_alloc(struct xlnx_video_buffers_pool *pool, + size_t size, size_t align); + +#endif /* __XLNX_VIDEO_BUFFERS_POOL_H__ */ diff --git a/lib/xlnx-events.c b/lib/xlnx-events.c new file mode 100644 index 0000000..933f27a --- /dev/null +++ b/lib/xlnx-events.c @@ -0,0 +1,269 @@ +/* + * Generic Event Loop + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _DEFAULT_SOURCE +#include <errno.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/select.h> + +#include <xlnx-events.h> +#include <xlnx-list.h> +#include <xlnx-tools.h> + +#include "xlnx-priv.h" + +#define SELECT_TIMEOUT 2000 /* in milliseconds */ + +struct xlnx_events { + struct list_entry events; + bool done; + + int maxfd; + fd_set rfds; + fd_set wfds; + fd_set efds; +}; + +struct xlnx_event_fd { + struct list_entry list; + + int fd; + int type; + void (*callback)(void *priv); + void *priv; +}; + +static void xlnx_events_watch_fd(void *handler, int fd, int event_type, + void(*callback)(void *), void *priv) +{ + struct xlnx_events *events = handler; + struct xlnx_event_fd *event; + + event = malloc(sizeof *event); + if (event == NULL) + return; + + event->fd = fd; + event->type = event_type; + event->callback = callback; + event->priv = priv; + + switch (event->type) { + case POLLIN: + FD_SET(fd, &events->rfds); + break; + case POLLOUT: + FD_SET(fd, &events->wfds); + break; + case POLLERR: + FD_SET(fd, &events->efds); + break; + } + + events->maxfd = max(events->maxfd, fd); + + list_append(&event->list, &events->events); +} + +static void xlnx_events_unwatch_fd(void *handler, int fd) +{ + struct xlnx_events *events = handler; + struct xlnx_event_fd *event = NULL; + struct xlnx_event_fd *entry; + int maxfd = 0; + + list_for_each_entry(entry, &events->events, list) { + if (entry->fd == fd) + event = entry; + else + maxfd = max(maxfd, entry->fd); + } + + if (event == NULL) + return; + + switch (event->type) { + case POLLIN: + FD_CLR(fd, &events->rfds); + break; + case POLLOUT: + FD_CLR(fd, &events->wfds); + break; + case POLLERR: + FD_CLR(fd, &events->efds); + break; + } + + events->maxfd = maxfd; + + list_remove(&event->list); + free(event); +} + +static void xlnx_events_dispatch(struct xlnx_events *events, const fd_set *rfds, + const fd_set *wfds, const fd_set *efds) +{ + struct xlnx_event_fd *event; + struct xlnx_event_fd *next; + + list_for_each_entry_safe(event, next, &events->events, list) { + if (event->type == POLLIN && FD_ISSET(event->fd, rfds)) + event->callback(event->priv); + else if (event->type == POLLOUT && FD_ISSET(event->fd, wfds)) + event->callback(event->priv); + else if (event->type == POLLERR && FD_ISSET(event->fd, efds)) + event->callback(event->priv); + + /* If the callback stopped events processing, we're done. */ + if (events->done) + break; + } +} + +/** + * \brief Start and run the event loop + * \param xv the library context + * + * This function starts and runs the event loop synchronously, waiting for + * events and dispatching them as they arrive. It doesn't return until the loop + * is requested to stop by a call to xlnx_events_stop() or a fatal error occurs. + * + * \return false if the loop exited due to a stop request, or true if it exited + * due to a fatal error. + */ +bool xlnx_events_loop(struct xlnx_video *xv) +{ + struct xlnx_events *events = xlnx_video_get_event_handler_data(xv); + + events->done = false; + + while (!events->done) { + struct timeval timeout; + fd_set rfds; + fd_set wfds; + fd_set efds; + int ret; + + timeout.tv_sec = SELECT_TIMEOUT / 1000; + timeout.tv_usec = (SELECT_TIMEOUT % 1000) * 1000; + rfds = events->rfds; + wfds = events->wfds; + efds = events->efds; + + ret = select(events->maxfd + 1, &rfds, &wfds, &efds, &timeout); + if (ret < 0) { + /* EINTR means that a signal has been received, continue + * to the next iteration in that case. + */ + if (errno == EINTR) + continue; + + printf("error: select failed with %d\n", errno); + break; + } + if (ret == 0) { + /* select() should never time out as the library is + * supposed to process images continuously. A timeout is + * thus considered as a fatal error. + */ + printf("error: select timeout\n"); + break; + } + + xlnx_events_dispatch(events, &rfds, &wfds, &efds); + } + + return !events->done; +} + +/** + * \brief Stop the event loop + * \param xv the library context + * + * Stop the running event loop started with xlnx_events_loop(). The loop is + * stopped asynchronously after processing the next event. This function doesn't + * wait for the loop to exit before returning. + */ +void xlnx_events_stop(struct xlnx_video *xv) +{ + struct xlnx_events *events = xlnx_video_get_event_handler_data(xv); + + events->done = true; +} + +static const struct xlnx_video_event_operations xlnx_events_ops = { + .watch = xlnx_events_watch_fd, + .unwatch = xlnx_events_unwatch_fd, +}; + +/** + * \brief Initialize event handling + * \param xv the library context + * + * This function registers event handling operations with the library context + * \a xv. The event handling implementation is based on a select() loop that is + * controlled through the xlnx_events_loop() and xlnx_events_stop() functions. + * + * Before destroying the library context event handling must be cleaned up with + * a call to xlnx_events_fini(). + * + * \return 0 on success or a negative error code on failure. + */ +int xlnx_events_init(struct xlnx_video *xv) +{ + struct xlnx_events *events; + + events = calloc(1, sizeof(*events)); + if (!events) + return -ENOMEM; + + FD_ZERO(&events->rfds); + FD_ZERO(&events->wfds); + FD_ZERO(&events->efds); + events->maxfd = 0; + list_init(&events->events); + + xlnx_video_set_event_handler(xv, &xlnx_events_ops, events); + return 0; +} + +/** + * \brief Clean up event handling + * \param xv the library context + * + * This function cleans up event handling. It must be called before destroying + * any library context previously initialized by a call to xlnx_events_init(). + */ +void xlnx_events_fini(struct xlnx_video *xv) +{ + struct xlnx_events *events = xlnx_video_get_event_handler_data(xv); + + if (events) { + xlnx_video_set_event_handler(xv, NULL, NULL); + free(events); + } +} diff --git a/lib/xlnx-plugin.c b/lib/xlnx-plugin.c new file mode 100644 index 0000000..bd4553d --- /dev/null +++ b/lib/xlnx-plugin.c @@ -0,0 +1,220 @@ +/* + * Xilinx Video Library - Plugin API + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <dirent.h> +#include <dlfcn.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> + +#include <mediactl.h> + +#include <xlnx-plugin.h> +#include <xlnx-video.h> + +#include "xlnx-priv.h" + +#define __stringify(n) #n +#define _stringify(n) __stringify(n) + +#define XLNX_VIDEO_PLUGIN_DIR "/usr/lib/xilinx/video/plugins" +#define XLNX_VIDEO_PLUGIN_SYMBOL_NAME _stringify(XLNX_VIDEO_PLUGIN_SYMBOL) + +struct xlnx_video_plugin { + struct list_entry list; + void *handle; + const struct xlnx_video_plugin_ops *ops; + const struct xlnx_video_plugin_info *info; +}; + +struct xlnx_video_component *xlnx_video_plugin_scan(struct xlnx_video *xv, + struct media_device *mdev) +{ + const struct media_device_info *mdev_info = media_get_info(mdev); + struct xlnx_video_component *xvcomp; + struct xlnx_video_plugin *plugin; + + xlnx_dbg(xv, "Searching plugin for media device %s (%s)\n", + mdev_info->model, mdev_info->bus_info); + + list_for_each_entry(plugin, &xv->plugins, list) { + xlnx_dbg(xv, "Trying plugin `%s'\n", plugin->info->name); + xvcomp = plugin->ops->scan(xv, mdev); + if (xvcomp) { + xlnx_dbg(xv, "Plugin supports media device\n"); + return xvcomp; + } + } + + xlnx_dbg(xv, "No matching plugin found\n"); + return NULL; +} + +static int xlnx_video_plugin_load_file(struct xlnx_video *xv, const char *filename) +{ + const struct xlnx_video_plugin_ops *plugin_ops; + struct xlnx_video_plugin *plugin; + void *plugin_so; + int ret; + + xlnx_dbg(xv, "Loading plugin from file %s\n", filename); + + plugin_so = dlopen(filename, RTLD_LAZY); + if (!plugin_so) { + xlnx_dbg(xv, "Failed to open shared library (%s)\n", dlerror()); + return -EINVAL; + } + + plugin_ops = dlsym(plugin_so, XLNX_VIDEO_PLUGIN_SYMBOL_NAME); + if (!plugin_ops) { + xlnx_dbg(xv, "Plugin symbol not found (%s)\n", dlerror()); + ret = -EINVAL; + goto error; + } + + plugin = calloc(1, sizeof(*plugin)); + if (!plugin) { + ret = -ENOMEM; + goto error; + } + + plugin->handle = plugin_so; + plugin->ops = plugin_ops; + plugin->info = plugin_ops->info(); + + list_append(&plugin->list, &xv->plugins); + + xlnx_dbg(xv, "Plugin loaded successfully\n"); + return 0; + +error: + dlclose(plugin_so); + return ret; +} + +/** + * \brief Load plugins from a given directory + * \param xv the library context + * \param path the full path name to the directory containing plugins + * + * This function attempts to load all plugins from the \a path directory. A + * plugin is a shared object whose name ends with '.so' and contains a symbol + * named '__xlnx_video_plugin' pointing to a struct xlnx_video_plugin_ops + * instance. + * + * When a valid plugin is found and loaded, call the init function supplied in + * the plugin operations structure. + * + * Shared objects that fail to load, don't export the plugin operations + * structure, don't implement the init function or return an error from their + * init function are ignored and not considered as a fatal error. + * + * \return Return the number of plugins successfully loaded, or a negative error + * code if a fatal error occurs. + */ +int xlnx_video_plugin_load_directory(struct xlnx_video *xv, const char *path) +{ + struct dirent *entry; + unsigned int count = 0; + size_t pathlen; + DIR *dir; + int ret = 0; + + xlnx_dbg(xv, "Loading plugins from directory %s\n", path); + + dir = opendir(path); + if (!dir) { + xlnx_dbg(xv, "Failed to open directory\n"); + return -errno; + } + + pathlen = strlen(path); + + while ((entry = readdir(dir))) { + char *filename; + size_t len; + + if (entry->d_type != DT_REG && entry->d_type != DT_LNK && + entry->d_type != DT_UNKNOWN) + continue; + + len = strlen(entry->d_name); + if (strcmp(entry->d_name + len - 3, ".so")) + continue; + + filename = malloc(pathlen + 1 + len + 1); + if (!filename) + break; + + strcpy(filename, path); + filename[pathlen] = '/'; + strcpy(filename + pathlen + 1, entry->d_name); + + ret = xlnx_video_plugin_load_file(xv, filename); + free(filename); + + if (ret < 0) + continue; + + count++; + } + + closedir(dir); + + return ret < 0 ? ret : (int)count; +} + +/** \cond INTERNAL_API */ + +/** + * \brief Initialize plugin support + * \param xv the library context + * + * Initialize plugin support and load plugins from the system default plugin + * directory ('/usr/lib/xilinx/video/plugins' by default). + * + * \return 0 on success or a negative error code if an error occurs. + */ +int xlnx_video_plugin_init(struct xlnx_video *xv) +{ + xlnx_video_plugin_load_directory(xv, XLNX_VIDEO_PLUGIN_DIR); + + return 0; +} + +/** + * \brief Initialize plugin support + * \param xv the library context + * + * Initialize plugin support and load plugins from the system default plugin + * directory ('/usr/lib/xilinx/video/plugins' by default). + * + * \return 0 on success or a negative error code if an error occurs. + */ +void xlnx_video_plugin_fini(struct xlnx_video *xv) +{ + struct xlnx_video_plugin *plugin, *next; + + list_for_each_entry_safe(plugin, next, &xv->plugins, list) { + list_remove(&plugin->list); + dlclose(plugin->handle); + free(plugin); + } +} + +/** \endcond */ diff --git a/lib/xlnx-priv.h b/lib/xlnx-priv.h new file mode 100644 index 0000000..ca83425 --- /dev/null +++ b/lib/xlnx-priv.h @@ -0,0 +1,107 @@ +/* + * Xilinx Video Library - Internal API + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#ifndef __XLNX_PRIV_H__ +#define __XLNX_PRIV_H__ +/** \cond INTERNAL_API */ + +#include <stdbool.h> +#include <stdlib.h> + +#include <xlnx-list.h> +#include <xlnx-video.h> + +struct media_device; +struct media_entity; +struct media_entity_desc; + +/** + * \brief Xilinx video entity in a component + */ +struct xlnx_video_entity { + /** Entities list */ + struct list_entry list; + /** Media entity */ + struct media_entity *entity; + /** Media entity information */ + const struct media_entity_desc *info; + /** Indicates whether this is the last entity in the component */ + bool last; +}; + +/** + * \brief Xilinx video component + */ +struct xlnx_video_component { + /** The library context */ + struct xlnx_video *xv; + /** List entry in the context's components list */ + struct list_entry list; + /** List of video entities in the component */ + struct list_entry entities; + + /** Component type */ + enum xlnx_video_component_type type; + /** Component name */ + const char *name; + + /** + * Entity at the input of the component (NULL if the component has no + * input entity). + */ + struct xlnx_video_entity *input; + /** + * Entity at the output of the component (NULL if the component has no + * output entity). + */ + struct xlnx_video_entity *output; +}; + +/** + * \brief Xilinx video library context + */ +struct xlnx_video { + /** List of video components in the system */ + struct list_entry components; + /** Number of components in the components list */ + unsigned int num_components; + + /** List of loaded plugins */ + struct list_entry plugins; + + /** Event handler operations */ + const struct xlnx_video_event_operations *event_ops; + /** Event handler private data */ + void *event_handler; + + /** Log level for library messages */ + enum xlnx_log_level log_level; +}; + +int xlnx_video_plugin_init(struct xlnx_video *xv); +void xlnx_video_plugin_fini(struct xlnx_video *xv); +struct xlnx_video_component *xlnx_video_plugin_scan(struct xlnx_video *xv, + struct media_device *mdev); + +void xlnx_video_log_print(struct xlnx_video *xv, unsigned int level, + const char *func, const char *fmt, ...); + +#define xlnx_dbg(xv, fmt...) \ + xlnx_video_log_print(xv, XLNX_LOG_DEBUG, __func__, fmt) + +/** \endcond INTERNAL_API */ +#endif /* __XLNX_PRIV_H__ */ diff --git a/lib/xlnx-video.c b/lib/xlnx-video.c new file mode 100644 index 0000000..61c9f7c --- /dev/null +++ b/lib/xlnx-video.c @@ -0,0 +1,388 @@ +/* + * Xilinx Video Library + * + * Copyright (C) 2014-2016 Ideas on board Oy + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <media-enumerate.h> +#include <mediactl.h> + +#include <xlnx-list.h> +#include <xlnx-plugin.h> +#include <xlnx-tools.h> +#include <xlnx-video.h> + +#include "xlnx-priv.h" + +/** + * \mainpage + * + * The Xilinx video library offers a high-level API to enumerate video-related + * components in the system, assemble them in pipelines and stream video in and + * out of them. + * + * To use the library, an application must create a library context with a call + * to xlnx_video_create(). The function returns a context pointer that will be + * passed to all other library functions. Once a context isn't needed anymore it + * should be destroyed by a call to xlnx_video_destroy(). + * + * Multiple contexts can be created and managed separately. However, as contexts + * store information about all media devices present in the system, accessing + * the same media devices from multiple contexts isn't supported and leads to + * undefined behaviour. + * + * Right after creation contexts are not ready for use. They must be initialized + * by registering event handling operations with xlnx_video_set_event_handler(). + * Optionally logging can be initialized if desired with + * xlnx_video_set_log_level(). Finally, to complete initialization applications + * must call xlnx_video_setup() on the context. From that point onward the + * context can be used with other library functions. + */ + +/* ----------------------------------------------------------------------------- + * Components Discovery and Management + */ + +/** + * \brief Get a list of components in the system + * \param xv the library context + * \param type the desired component type + * \param [out] components array of returned components + * + * Store a list of components matching the requested \a type in the + * \a components array. + * + * The \a type parameter is a bitmask of components type. Components matching + * one of the types in the bitmask will be included in the list, all other + * components will be ignored. + * + * The \a components parameter points to an array of components pointer. + * The function allocates an array of component pointers, fills it with + * pointers to all components matching the requested \a type, and stores it in + * the \a components pointer. The components array is allocated using the C + * memory allocator and must be freed by the caller with a call to free(). + * + * The \a components array pointer can be NULL, in which case the function will + * not allocate any memory and will only return the number of matching + * components. + * + * If no component matches the requested \a type this function will not allocate + * any memory and will not touch the \a components parameter. + * + * Components must first be enumerated by a call to + * xlnx_video_component_enumerate() before calling this function. + * + * \return the total number of components matching the requested \a type found + * in the system and supported by the library (including the loaded plugins) on + * success, or a negative error code on failure. + */ +int xlnx_video_component_list(struct xlnx_video *xv, + enum xlnx_video_component_type type, + struct xlnx_video_component ***components) +{ + struct xlnx_video_component **comps; + struct xlnx_video_component *xvcomp; + unsigned int count; + + if (!xv->num_components) + return 0; + + count = 0; + list_for_each_entry(xvcomp, &xv->components, list) { + if (!(xvcomp->type & type)) + continue; + + count++; + } + + if (!count || !components) + return count; + + comps = calloc(count, sizeof(*comps)); + if (comps == NULL) + return -ENOMEM; + + count = 0; + list_for_each_entry(xvcomp, &xv->components, list) { + if (!(xvcomp->type & type)) + continue; + + comps[count++] = xvcomp; + } + + *components = comps; + return count; +} + +/** + * \brief Get the component name + * \param xvcomp the component + * + * \return the name of the component \a xvcomp. The name string is valid for the + * lifetime of the component. + */ +const char *xlnx_video_component_name(const struct xlnx_video_component *xvcomp) +{ + return xvcomp->name; +} + +/** + * \brief Get the component type + * \param xvcomp the component + * + * \return the type of the component \a xvcomp. + */ +enum xlnx_video_component_type +xlnx_video_component_type(const struct xlnx_video_component *xvcomp) +{ + return xvcomp->type; +} + +/* ----------------------------------------------------------------------------- + * Components Discovery + */ + +static int xlnx_video_component_scan_media(struct xlnx_video *xv, + struct media_device *media) +{ + struct xlnx_video_component *xvcomp; + + xvcomp = xlnx_video_plugin_scan(xv, media); + if (xvcomp) { + list_append(&xv->components, &xvcomp->list); + xv->num_components++; + } + + return 0; +} + +static int xlnx_video_component_scan(struct xlnx_video *xv) +{ + struct media_enumerate *media_enum; + struct media_device **devices; + unsigned int num_devices; + unsigned int i; + int ret; + + /* Start by scanning media controller devices. */ + media_enum = media_enumerate_new(); + if (!media_enum) + return -ENOMEM; + + ret = media_enumerate_scan(media_enum); + if (ret < 0) + goto done; + + num_devices = media_enumerate_get_devices_count(media_enum); + if (num_devices == 0) { + ret = -ENXIO; + goto done; + } + + devices = media_enumerate_get_devices(media_enum); + + for (i = 0; i < num_devices; ++i) { + ret = xlnx_video_component_scan_media(xv, devices[i]); + if (ret < 0) + goto done; + } + +done: + media_enumerate_unref(media_enum); + return ret; +} + +/** + * \brief Enumerate components in the system + * \param xv the library context + * + * This function enumerates all supported components in the system and cache + * them in the library context \a xv. It must be called once after initializing + * the context and before calling any other of the component-related functions. + * + * \return 0 on success or a negative error code otherwise. + */ +int xlnx_video_component_enumerate(struct xlnx_video *xv) +{ + int ret; + + ret = xlnx_video_component_scan(xv); + if (ret < 0) + return ret; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +/** + * \brief Create a working context for the library + * + * The Xilinx video library uses context objects to store internal data. + * Applications must call this function to create and initialize a context + * that can then be passed to other library functions. + * + * Multiple contexts can be created and managed separately. However, as contexts + * store information about all media devices present in the system, accessing + * the same media devices from multiple contexts isn't supported and leads to + * undefined behaviour. + * + * When a context isn't needed anymore applications must call the + * xlnx_video_destroy() function to destroy of it. + * + * \return a context pointer on success or NULL if an error occurs. + */ +struct xlnx_video *xlnx_video_create(void) +{ + struct xlnx_video *xv; + + xv = calloc(1, sizeof *xv); + if (!xv) + return NULL; + + list_init(&xv->components); + list_init(&xv->plugins); + + xv->log_level = XLNX_LOG_NONE; + + return xv; +} + +/** + * \brief Setup a library context to make it ready for use + * \param xv the library context + * + * After creating and initializing a library context \a xv, applications must + * call this function to finish context setup and make it ready for use. + * + * \return 0 on success or a negative error code otherwise. + */ +int xlnx_video_setup(struct xlnx_video *xv) +{ + int ret; + + ret = xlnx_video_plugin_init(xv); + if (ret < 0) + return ret; + + return 0; +} + +/** + * \brief Destroy a library context + * \param xv the library context + * + * Applications must call this function to destroy library contexts created + * by xlnx_video_create() when they're not needed anymore, and at the latest + * when the application exits. The context pointer \a xv becomes invalid when + * this function is called. + */ +void xlnx_video_destroy(struct xlnx_video *xv) +{ + xlnx_video_plugin_fini(xv); + free(xv); +} + +/** + * \brief Register event handler operations + * \param xv the library context + * \param ops the event handling operations + * \param handler the event handler private data + * + * The library requires the application to provide an implementation of a set of + * operations to handle event notifications on file descriptors. + * + * This function sets the event handler operations \a ops and private data + * pointer \a handler for the library context. It must be called after creating + * a context with xlnx_video_create() and before initializing it with + * xlnx_video_init(). + */ +void xlnx_video_set_event_handler(struct xlnx_video *xv, + const struct xlnx_video_event_operations *ops, + void *handler) +{ + xv->event_ops = ops; + xv->event_handler = handler; +} + +/** + * \brief Retrieve the event handler private data registered with the context + * \param xv the library context + * + * \return the event handler private data pointer registered with the library + * context by a previous call to xlnx_video_set_event_handler(). + */ +void *xlnx_video_get_event_handler_data(struct xlnx_video *xv) +{ + return xv->event_handler; +} + +/** + * \brief Log a library message + * \param xv the library context + * \param level the message log level + * \param fmt the message format + * \param ... the message arguments + * + * Log the message specified by \a format and \a ... arguments using the library + * logging infrastructure. The message will be logged only if its \a level is + * lower than or equal to the \a xv library context current log level, as set by + * xlnx_log_set_level(). Otherwise the message will be dropped silently. + */ +void xlnx_video_log_print(struct xlnx_video *xv, unsigned int level, + const char *func, const char *fmt, ...) +{ + struct timespec ts; + va_list ap; + + if (level == XLNX_LOG_NONE || level > xv->log_level) + return; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + printf("[%lu.%06lu] %s: ", ts.tv_sec, ts.tv_nsec / 1000, func); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +/** + * \brief Set the library context log level + * \param xv the library context + * \param level the desired log level + * + * The Xilinx video library can print error, informational and debug messages + * to help debugging applications and library internals. Only messages whose log + * level is lower than or equal to the library context log level are printed, + * other messages are dropped silently + * + * This function sets the active log level for the context \a xv to \a level. + * The default log level is XLNX_LOG_NONE, which drops all log messages. + */ +void xlnx_video_set_log_level(struct xlnx_video *xv, enum xlnx_log_level level) +{ + xv->log_level = level; +} |