diff options
author | Daniel Scally <dan.scally@ideasonboard.com> | 2022-11-30 13:31:08 +0000 |
---|---|---|
committer | Daniel Scally <dan.scally@ideasonboard.com> | 2022-11-30 13:48:26 +0000 |
commit | 1c18c451f3a48507e876cfdb03c0a6f2187f62cb (patch) | |
tree | 1ea466f89e854c35236b7a229981f123a6b8496d | |
parent | 90955f728061d0cbec4b470849a7f564637a6e3a (diff) |
lib/timer: Add timer infrastructure
Some sources simply fill the buffers passed by the USB subsystem and
return them as quickly as possible. Particularly with compressed
formats operating at superspeed this rapidly results in unrealistic
frame rates.
Add infrastructure that allows us to define a specific
framerate and introduce blocking calls that constrain those sources
to the framerates expected by the host.
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
-rw-r--r-- | include/uvcgadget/meson.build | 1 | ||||
-rw-r--r-- | include/uvcgadget/timer.h | 59 | ||||
-rw-r--r-- | lib/meson.build | 1 | ||||
-rw-r--r-- | lib/timer.c | 96 |
4 files changed, 157 insertions, 0 deletions
diff --git a/include/uvcgadget/meson.build b/include/uvcgadget/meson.build index c79ea18..d5aaf17 100644 --- a/include/uvcgadget/meson.build +++ b/include/uvcgadget/meson.build @@ -5,6 +5,7 @@ uvcgadget_public_headers = files([ 'events.h', 'list.h', 'stream.h', + 'timer.h', 'v4l2-source.h', 'video-source.h', ]) diff --git a/include/uvcgadget/timer.h b/include/uvcgadget/timer.h new file mode 100644 index 0000000..de93287 --- /dev/null +++ b/include/uvcgadget/timer.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libuvcgadget timer utils + * + * Copyright (C) 2022 Daniel Scally + * + * Contact: Daniel Scally <dan.scally@ideasonboard.com> + */ + +struct timer; + +/* + * timer_new - Create a new timer + * + * Allocates and returns a new struct timer. This must be configured with + * timer_set_fps() and then armed with timer_arm(), following which calls to + * timer_wait() will block until the expiration of a period as defined by + * timer_set_fps(). + * + * Timers allocated with this function should be removed with timer_destroy() + */ +struct timer *timer_new(void); + +/* + * timer_set_fps - Configure the timer's wait period + * + * Configure the timer to wait for a period of time such that expirations per + * second matches @fps + */ +void timer_set_fps(struct timer *timer, int fps); + +/* + * timer_arm + * + * Arms the timer such that calls to timer_wait() become blocking until the + * expiration of a period. + */ +int timer_arm(struct timer *timer); + +/* + * timer_disarm + * + * Disarms the timer such that calls to timer_wait() return without blocking. + */ +int timer_disarm(struct timer *timer); + +/* + * timer_wait + * + * If the timer is armed, block until the expiration of a period + */ +void timer_wait(struct timer *timer); + +/* + * timer_destroy + * + * Close the timer's file descriptor and free the memory + */ +void timer_destroy(struct timer *timer); diff --git a/lib/meson.build b/lib/meson.build index f6991d5..a0d67d5 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -6,6 +6,7 @@ libuvcgadget_sources = files([ 'jpg-source.c', 'stream.c', 'test-source.c', + 'timer.c', 'uvc.c', 'v4l2.c', 'v4l2-source.c', diff --git a/lib/timer.c b/lib/timer.c new file mode 100644 index 0000000..6627656 --- /dev/null +++ b/lib/timer.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libuvcgadget timer utils + * + * Copyright (C) 2022 Daniel Scally + * + * Contact: Daniel Scally <dan.scally@ideasonboard.com> + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/timerfd.h> +#include <unistd.h> + +#include "timer.h" + +struct timer { + int fd; + struct itimerspec settings; +}; + +struct timer *timer_new(void) +{ + struct timer *timer; + + timer = malloc(sizeof(*timer)); + if (!timer) + return NULL; + + memset(timer, 0, sizeof(*timer)); + + timer->fd = timerfd_create(CLOCK_REALTIME, 0); + if (timer->fd < 0) { + fprintf(stderr, "failed to create timer: %s (%d)\n", + strerror(errno), errno); + goto err_free_timer; + } + + return timer; + +err_free_timer: + free(timer); + + return NULL; +} + +void timer_set_fps(struct timer *timer, int fps) +{ + int ns_per_frame = 1000000000 / fps; + + timer->settings.it_value.tv_nsec = ns_per_frame; + timer->settings.it_interval.tv_nsec = ns_per_frame; +} + +int timer_arm(struct timer *timer) +{ + int ret; + + ret = timerfd_settime(timer->fd, 0, &timer->settings, NULL); + if (ret) + fprintf(stderr, "failed to change timer settings: %s (%d)\n", + strerror(errno), errno); + + return ret; +} + +int timer_disarm(struct timer *timer) +{ + static const struct itimerspec disable_settings = { + { 0, 0 }, + { 0, 0 }, + }; + int ret; + + ret = timerfd_settime(timer->fd, 0, &disable_settings, NULL); + if (ret) + fprintf(stderr, "failed to disable timer: %s (%d)\n", + strerror(errno), errno); + + return ret; +} + +void timer_wait(struct timer *timer) +{ + char read_buf[8]; + + read(timer->fd, read_buf, 8); +} + +void timer_destroy(struct timer *timer) +{ + close(timer->fd); + free(timer); +} |