Compare commits

...

17 Commits
v0.57 ... v0.62

Author SHA1 Message Date
Devaev Maxim
acc8628f3d Bump version: 0.61 → 0.62 2019-03-19 22:33:06 +03:00
Devaev Maxim
46e99be201 Supported XSI strerror_r() 2019-03-19 22:26:30 +03:00
Devaev Maxim
7fbeca41fa refactoring 2019-03-18 21:43:23 +03:00
Devaev Maxim
73ceba77a8 Bump version: 0.60 → 0.61 2019-03-18 00:30:13 +03:00
Devaev Maxim
a3e5d17628 redefineable XIOCTL_RETRIES 2019-03-17 22:54:26 +03:00
Devaev Maxim
c30dea20a5 Bump version: 0.59 → 0.60 2019-03-17 20:10:11 +03:00
Devaev Maxim
b31450ba41 size -> used 2019-03-17 19:44:49 +03:00
Devaev Maxim
c05457ce2f separate hw_buffer_t size and allocated 2019-03-17 19:33:56 +03:00
Devaev Maxim
9e63076ec5 fixed omx slice_size 2019-03-17 18:50:12 +03:00
Devaev Maxim
92844fc3db Bump version: 0.58 → 0.59 2019-03-17 15:22:30 +03:00
Devaev Maxim
7bb9434850 shorten log about stream clients 2019-03-17 15:06:08 +03:00
Devaev Maxim
3104a00913 Bump version: 0.57 → 0.58 2019-03-16 19:20:39 +03:00
Devaev Maxim
28c51f3f2f better help 2019-03-16 16:27:45 +03:00
Devaev Maxim
5c035f21c8 some short options 2019-03-16 15:28:11 +03:00
Devaev Maxim
52ecaf7fd3 refactoring 2019-03-16 13:22:06 +03:00
Devaev Maxim
aa007c676f limited default workers and buffers number 2019-03-16 13:21:44 +03:00
Devaev Maxim
1f0b49e5fe refactoring 2019-03-16 13:09:30 +03:00
15 changed files with 215 additions and 195 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion] [bumpversion]
commit = True commit = True
tag = True tag = True
current_version = 0.57 current_version = 0.62
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)? parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
serialize = serialize =
{major}.{minor} {major}.{minor}

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer pkgname=ustreamer
pkgver=0.57 pkgver=0.62
pkgrel=1 pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer" pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pi-kvm/ustreamer" url="https://github.com/pi-kvm/ustreamer"

View File

@@ -22,4 +22,4 @@
#pragma once #pragma once
#define VERSION "0.57" #define VERSION "0.62"

View File

@@ -95,7 +95,7 @@ struct device_t *device_init() {
dev->height = 480; dev->height = 480;
dev->format = V4L2_PIX_FMT_YUYV; dev->format = V4L2_PIX_FMT_YUYV;
dev->standard = V4L2_STD_UNKNOWN; dev->standard = V4L2_STD_UNKNOWN;
dev->n_buffers = max_u(sysconf(_SC_NPROCESSORS_ONLN), 1) + 1; dev->n_buffers = max_u(min_u(sysconf(_SC_NPROCESSORS_ONLN), 4), 1) + 1;
dev->n_workers = dev->n_buffers; dev->n_workers = dev->n_buffers;
dev->timeout = 1; dev->timeout = 1;
dev->error_delay = 1; dev->error_delay = 1;
@@ -177,8 +177,8 @@ void device_close(struct device_t *dev) {
for (unsigned index = 0; index < dev->run->n_buffers; ++index) { for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next # define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
if (HW_BUFFER(size) > 0 && HW_BUFFER(data) != MAP_FAILED) { if (HW_BUFFER(allocated) > 0 && HW_BUFFER(data) != MAP_FAILED) {
if (munmap(HW_BUFFER(data), HW_BUFFER(size)) < 0) { if (munmap(HW_BUFFER(data), HW_BUFFER(allocated)) < 0) {
LOG_PERROR("Can't unmap device buffer %u", index); LOG_PERROR("Can't unmap device buffer %u", index);
} }
} }
@@ -201,6 +201,77 @@ void device_close(struct device_t *dev) {
} }
} }
int device_switch_capturing(struct device_t *dev, bool enable) {
if (enable != dev->run->capturing) {
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
LOG_DEBUG("Calling ioctl(%s) ...", (enable ? "VIDIOC_STREAMON" : "VIDIOC_STREAMOFF"));
if (xioctl(dev->run->fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
LOG_PERROR("Unable to %s capturing", (enable ? "start" : "stop"));
if (enable) {
return -1;
}
}
dev->run->capturing = enable;
LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
}
return 0;
}
int device_grab_buffer(struct device_t *dev) {
struct v4l2_buffer buf_info;
MEMSET_ZERO(buf_info);
buf_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf_info.memory = V4L2_MEMORY_MMAP;
LOG_DEBUG("Calling ioctl(VIDIOC_DQBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_DQBUF, &buf_info) < 0) {
LOG_PERROR("Unable to dequeue buffer");
return -1;
}
LOG_DEBUG("Got a new frame in buffer index=%u; bytesused=%u", buf_info.index, buf_info.bytesused);
if (buf_info.index >= dev->run->n_buffers) {
LOG_ERROR("Got invalid buffer index=%u; nbuffers=%u", buf_info.index, dev->run->n_buffers);
return -1;
}
dev->run->hw_buffers[buf_info.index].used = buf_info.bytesused;
memcpy(&dev->run->hw_buffers[buf_info.index].buf_info, &buf_info, sizeof(struct v4l2_buffer));
return buf_info.index;
}
int device_release_buffer(struct device_t *dev, unsigned index) {
LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_QBUF, &dev->run->hw_buffers[index].buf_info) < 0) {
LOG_PERROR("Unable to requeue buffer");
return -1;
}
dev->run->hw_buffers[index].used = 0;
return 0;
}
int device_consume_event(struct device_t *dev) {
struct v4l2_event event;
LOG_DEBUG("Calling ioctl(VIDIOC_DQEVENT) ...");
if (!xioctl(dev->run->fd, VIDIOC_DQEVENT, &event)) {
switch (event.type) {
case V4L2_EVENT_SOURCE_CHANGE:
LOG_INFO("Got V4L2_EVENT_SOURCE_CHANGE: source changed");
return -1;
case V4L2_EVENT_EOS:
LOG_INFO("Got V4L2_EVENT_EOS: end of stream (ignored)");
return 0;
}
} else {
LOG_PERROR("Got some V4L2 device event, but where is it? ");
}
return 0;
}
static int _device_open_check_cap(struct device_t *dev) { static int _device_open_check_cap(struct device_t *dev) {
struct v4l2_capability cap; struct v4l2_capability cap;
int input = dev->input; // Needs pointer to int for ioctl() int input = dev->input; // Needs pointer to int for ioctl()
@@ -397,7 +468,7 @@ static int _device_open_mmap(struct device_t *dev) {
LOG_PERROR("Can't map device buffer %u", dev->run->n_buffers); LOG_PERROR("Can't map device buffer %u", dev->run->n_buffers);
return -1; return -1;
} }
HW_BUFFER(size) = buf_info.length; HW_BUFFER(allocated) = buf_info.length;
# undef HW_BUFFER # undef HW_BUFFER
} }

View File

@@ -42,13 +42,15 @@
struct hw_buffer_t { struct hw_buffer_t {
unsigned char *data; unsigned char *data;
size_t size; size_t used;
size_t allocated;
struct v4l2_buffer buf_info;
}; };
struct picture_t { struct picture_t {
unsigned char *data; unsigned char *data;
size_t size; size_t used;
size_t allocated; size_t allocated;
long double grab_time; long double grab_time;
long double encode_begin_time; long double encode_begin_time;
@@ -116,3 +118,8 @@ v4l2_std_id device_parse_standard(const char *str);
int device_open(struct device_t *dev); int device_open(struct device_t *dev);
void device_close(struct device_t *dev); void device_close(struct device_t *dev);
int device_switch_capturing(struct device_t *dev, bool enable);
int device_grab_buffer(struct device_t *dev);
int device_release_buffer(struct device_t *dev, unsigned index);
int device_consume_event(struct device_t *dev);

View File

@@ -81,8 +81,8 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
# define PICTURE(_next) dev->run->pictures[index]._next # define PICTURE(_next) dev->run->pictures[index]._next
PICTURE(size) = 0; PICTURE(used) = 0;
_jpeg_set_dest_picture(&jpeg, PICTURE(data), &PICTURE(size)); _jpeg_set_dest_picture(&jpeg, PICTURE(data), &PICTURE(used));
jpeg.image_width = dev->run->width; jpeg.image_width = dev->run->width;
jpeg.image_height = dev->run->height; jpeg.image_height = dev->run->height;
@@ -112,8 +112,8 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
// https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg // https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
jpeg_finish_compress(&jpeg); jpeg_finish_compress(&jpeg);
jpeg_destroy_compress(&jpeg); jpeg_destroy_compress(&jpeg);
assert(PICTURE(size) > 0); assert(PICTURE(used) > 0);
assert(PICTURE(size) <= dev->run->max_picture_size); assert(PICTURE(used) <= dev->run->max_picture_size);
} }
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, size_t *written) { static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, size_t *written) {

View File

@@ -69,8 +69,8 @@ void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) {
# define PICTURE(_next) dev->run->pictures[index]._next # define PICTURE(_next) dev->run->pictures[index]._next
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next # define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
assert(PICTURE(allocated) >= HW_BUFFER(size) + sizeof(HUFFMAN_TABLE)); assert(PICTURE(allocated) >= HW_BUFFER(used) + sizeof(HUFFMAN_TABLE));
PICTURE(size) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(size)); PICTURE(used) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(used));
# undef HW_BUFFER # undef HW_BUFFER
# undef PICTURE # undef PICTURE

View File

@@ -178,7 +178,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
# define OUT(_next) omx->output_buffer->_next # define OUT(_next) omx->output_buffer->_next
OMX_ERRORTYPE error; OMX_ERRORTYPE error;
size_t slice_size = IN(nAllocLen); size_t slice_size = (IN(nAllocLen) < HW_BUFFER(used) ? IN(nAllocLen) : HW_BUFFER(used));
size_t pos = 0; size_t pos = 0;
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) { if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
@@ -186,7 +186,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
return -1; return -1;
} }
PICTURE(size) = 0; PICTURE(used) = 0;
omx->output_available = false; omx->output_available = false;
omx->input_required = true; omx->input_required = true;
@@ -198,9 +198,9 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
if (omx->output_available) { if (omx->output_available) {
omx->output_available = false; omx->output_available = false;
assert(PICTURE(size) + OUT(nFilledLen) <= dev->run->max_picture_size); assert(PICTURE(used) + OUT(nFilledLen) <= dev->run->max_picture_size);
memcpy(PICTURE(data) + PICTURE(size), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen)); memcpy(PICTURE(data) + PICTURE(used), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
PICTURE(size) += OUT(nFilledLen); PICTURE(used) += OUT(nFilledLen);
if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) { if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) {
OUT(nFlags) = 0; OUT(nFlags) = 0;
@@ -216,7 +216,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
if (omx->input_required) { if (omx->input_required) {
omx->input_required = false; omx->input_required = false;
if (pos == HW_BUFFER(size)) { if (pos == HW_BUFFER(used)) {
continue; continue;
} }
@@ -226,8 +226,8 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
pos += slice_size; pos += slice_size;
if (pos + slice_size > HW_BUFFER(size)) { if (pos + slice_size > HW_BUFFER(used)) {
slice_size = HW_BUFFER(size) - pos; slice_size = HW_BUFFER(used) - pos;
} }
if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) { if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) {

View File

@@ -324,7 +324,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
# define EXPOSED(_next) server->run->exposed->_next # define EXPOSED(_next) server->run->exposed->_next
assert((buf = evbuffer_new())); assert((buf = evbuffer_new()));
assert(!evbuffer_add(buf, (const void *)EXPOSED(picture.data), EXPOSED(picture.size))); assert(!evbuffer_add(buf, (const void *)EXPOSED(picture.data), EXPOSED(picture.used)));
ADD_HEADER("Access-Control-Allow-Origin:", "*"); ADD_HEADER("Access-Control-Allow-Origin:", "*");
ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0"); ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0");
@@ -420,13 +420,8 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
} }
evhttp_connection_get_peer(conn, &client_addr, &client_port); evhttp_connection_get_peer(conn, &client_addr, &client_port);
LOG_INFO("HTTP: Registered the new stream client: [%s]:%u; id=%s; advance_headers=%s; dual_final_frames=%s; clients now: %u", LOG_INFO("HTTP: Registered the new stream client: [%s]:%u; id=%s; clients now: %u",
client_addr, client_addr, client_port, client->id, server->run->stream_clients_count);
client_port,
client->id,
bool_to_string(client->advance_headers),
bool_to_string(client->dual_final_frames),
server->run->stream_clients_count);
buf_event = evhttp_connection_get_bufferevent(conn); buf_event = evhttp_connection_get_bufferevent(conn);
bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client); bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client);
@@ -510,7 +505,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"Content-Length: %zu" RN "Content-Length: %zu" RN
"X-Timestamp: %.06Lf" RN "X-Timestamp: %.06Lf" RN
"%s", "%s",
EXPOSED(picture.size) * sizeof(*EXPOSED(picture.data)), EXPOSED(picture.used) * sizeof(*EXPOSED(picture.data)),
get_now_real(), (client->extra_headers ? "" : RN) get_now_real(), (client->extra_headers ? "" : RN)
)); ));
if (client->extra_headers) { if (client->extra_headers) {
@@ -546,7 +541,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
assert(!evbuffer_add(buf, assert(!evbuffer_add(buf,
(void *)EXPOSED(picture.data), (void *)EXPOSED(picture.data),
EXPOSED(picture.size) * sizeof(*EXPOSED(picture.data)) EXPOSED(picture.used) * sizeof(*EXPOSED(picture.data))
)); ));
assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN)); assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN));
@@ -665,7 +660,7 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv
if (atomic_load(&server->run->stream->updated)) { if (atomic_load(&server->run->stream->updated)) {
LOG_DEBUG("Refreshing HTTP exposed ..."); LOG_DEBUG("Refreshing HTTP exposed ...");
A_MUTEX_LOCK(&server->run->stream->mutex); A_MUTEX_LOCK(&server->run->stream->mutex);
if (server->run->stream->picture.size > 0) { // If online if (server->run->stream->picture.used > 0) { // If online
picture_updated = _expose_new_picture(server); picture_updated = _expose_new_picture(server);
UNLOCK_STREAM; UNLOCK_STREAM;
} else { } else {
@@ -688,19 +683,19 @@ static bool _expose_new_picture(struct http_server_t *server) {
# define STREAM(_next) server->run->stream->_next # define STREAM(_next) server->run->stream->_next
# define EXPOSED(_next) server->run->exposed->_next # define EXPOSED(_next) server->run->exposed->_next
assert(STREAM(picture.size) > 0); assert(STREAM(picture.used) > 0);
EXPOSED(captured_fps) = STREAM(captured_fps); EXPOSED(captured_fps) = STREAM(captured_fps);
EXPOSED(expose_begin_time) = get_now_monotonic(); EXPOSED(expose_begin_time) = get_now_monotonic();
# define MEM_STREAM_TO_EXPOSED \ # define MEM_STREAM_TO_EXPOSED \
EXPOSED(picture.data), STREAM(picture.data), \ EXPOSED(picture.data), STREAM(picture.data), \
STREAM(picture.size) * sizeof(*STREAM(picture.data)) STREAM(picture.used) * sizeof(*STREAM(picture.data))
if (server->drop_same_frames) { if (server->drop_same_frames) {
if ( if (
EXPOSED(online) EXPOSED(online)
&& EXPOSED(dropped) < server->drop_same_frames && EXPOSED(dropped) < server->drop_same_frames
&& EXPOSED(picture.size) == STREAM(picture.size) && EXPOSED(picture.used) == STREAM(picture.used)
&& !memcmp(MEM_STREAM_TO_EXPOSED) && !memcmp(MEM_STREAM_TO_EXPOSED)
) { ) {
EXPOSED(expose_cmp_time) = get_now_monotonic(); EXPOSED(expose_cmp_time) = get_now_monotonic();
@@ -725,7 +720,7 @@ static bool _expose_new_picture(struct http_server_t *server) {
# undef MEM_STREAM_TO_EXPOSED # undef MEM_STREAM_TO_EXPOSED
EXPOSED(picture.size) = STREAM(picture.size); EXPOSED(picture.used) = STREAM(picture.used);
EXPOSED(picture.grab_time) = STREAM(picture.grab_time); EXPOSED(picture.grab_time) = STREAM(picture.grab_time);
EXPOSED(picture.encode_begin_time) = STREAM(picture.encode_begin_time); EXPOSED(picture.encode_begin_time) = STREAM(picture.encode_begin_time);
@@ -753,7 +748,7 @@ static bool _expose_blank_picture(struct http_server_t *server) {
EXPOSED(expose_begin_time) = get_now_monotonic(); EXPOSED(expose_begin_time) = get_now_monotonic();
EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time); EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time);
if (EXPOSED(online) || EXPOSED(picture.size) == 0) { if (EXPOSED(online) || EXPOSED(picture.used) == 0) {
if (EXPOSED(picture.allocated) < BLANK_JPEG_LEN) { if (EXPOSED(picture.allocated) < BLANK_JPEG_LEN) {
A_REALLOC(EXPOSED(picture.data), BLANK_JPEG_LEN); A_REALLOC(EXPOSED(picture.data), BLANK_JPEG_LEN);
EXPOSED(picture.allocated) = BLANK_JPEG_LEN; EXPOSED(picture.allocated) = BLANK_JPEG_LEN;
@@ -761,7 +756,7 @@ static bool _expose_blank_picture(struct http_server_t *server) {
memcpy(EXPOSED(picture.data), BLANK_JPEG_DATA, BLANK_JPEG_LEN * sizeof(*EXPOSED(picture.data))); memcpy(EXPOSED(picture.data), BLANK_JPEG_DATA, BLANK_JPEG_LEN * sizeof(*EXPOSED(picture.data)));
EXPOSED(picture.size) = BLANK_JPEG_LEN; EXPOSED(picture.used) = BLANK_JPEG_LEN;
EXPOSED(picture.grab_time) = 0; EXPOSED(picture.grab_time) = 0;
EXPOSED(picture.encode_begin_time) = 0; EXPOSED(picture.encode_begin_time) = 0;

View File

@@ -25,16 +25,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <pthread.h> #include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include "tools.h" #include "tools.h"
@@ -72,7 +68,7 @@ pthread_mutex_t log_mutex;
} }
#define LOG_PRINTF_NOLOCK(_label, _msg, ...) { \ #define LOG_PRINTF_NOLOCK(_label, _msg, ...) { \
printf("-- " _label " [%.03Lf tid=%ld] -- " _msg "\n", get_now_monotonic(), syscall(SYS_gettid), ##__VA_ARGS__); \ printf("-- " _label " [%.03Lf tid=%d] -- " _msg "\n", get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \
fflush(stdout); \ fflush(stdout); \
} }
@@ -84,9 +80,9 @@ pthread_mutex_t log_mutex;
#define LOG_PERROR(_msg, ...) { \ #define LOG_PERROR(_msg, ...) { \
char _buf[1024] = ""; \ char _buf[1024] = ""; \
char *_ptr = strerror_r(errno, _buf, 1024); \ char *_ptr = errno_to_string(_buf, 1024); \
LOGGING_LOCK; \ LOGGING_LOCK; \
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", get_now_monotonic(), syscall(SYS_gettid), ##__VA_ARGS__, _ptr); \ printf("-- ERROR [%.03Lf tid=%d] -- " _msg ": %s\n", get_now_monotonic(), get_thread_id(), ##__VA_ARGS__, _ptr); \
fflush(stdout); \ fflush(stdout); \
LOGGING_UNLOCK; \ LOGGING_UNLOCK; \
} }
@@ -124,3 +120,13 @@ pthread_mutex_t log_mutex;
LOGGING_UNLOCK; \ LOGGING_UNLOCK; \
} \ } \
} }
INLINE char *errno_to_string(char *buf, size_t size) {
#if defined(__GLIBC__) && defined(_GNU_SOURCE)
return strerror_r(errno, buf, size);
#else
strerror_r(errno, buf, size);
return buf;
#endif
}

View File

@@ -41,7 +41,7 @@
#include "http.h" #include "http.h"
static const char _SHORT_OPTS[] = "d:i:x:y:m:a:f:z:ntb:w:q:c:s:p:u:ro:e:h"; static const char _SHORT_OPTS[] = "d:i:x:y:m:a:f:z:ntb:w:q:c:s:p:u:ro:e:lhv";
static const struct option _LONG_OPTS[] = { static const struct option _LONG_OPTS[] = {
{"device", required_argument, NULL, 'd'}, {"device", required_argument, NULL, 'd'},
{"input", required_argument, NULL, 'i'}, {"input", required_argument, NULL, 'i'},
@@ -80,7 +80,7 @@ static const struct option _LONG_OPTS[] = {
{"unix-rm", no_argument, NULL, 'r'}, {"unix-rm", no_argument, NULL, 'r'},
{"unix-mode", required_argument, NULL, 'o'}, {"unix-mode", required_argument, NULL, 'o'},
{"drop-same-frames", required_argument, NULL, 'e'}, {"drop-same-frames", required_argument, NULL, 'e'},
{"slowdown", no_argument, NULL, 3000}, {"slowdown", no_argument, NULL, 'l'},
{"fake-width", required_argument, NULL, 3001}, {"fake-width", required_argument, NULL, 3001},
{"fake-height", required_argument, NULL, 3002}, {"fake-height", required_argument, NULL, 3002},
{"server-timeout", required_argument, NULL, 3003}, {"server-timeout", required_argument, NULL, 3003},
@@ -90,7 +90,7 @@ static const struct option _LONG_OPTS[] = {
{"debug", no_argument, NULL, 5002}, {"debug", no_argument, NULL, 5002},
{"log-level", required_argument, NULL, 5010}, {"log-level", required_argument, NULL, 5010},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 6000}, {"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
@@ -106,78 +106,79 @@ static void _version(bool nl) {
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) { static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n"); printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n");
printf("===================================================\n\n"); printf("═══════════════════════════════════════════════════\n\n");
printf("Version: "); printf("Version: ");
_version(false); _version(false);
printf("; license: GPLv3\n"); printf("; license: GPLv3\n");
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n"); printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
printf("Capturing options:\n"); printf("Capturing options:\n");
printf("------------------\n"); printf("══════════════════\n");
printf(" -d|--device </dev/path> -- Path to V4L2 device. Default: %s.\n\n", dev->path); printf(" -d|--device </dev/path> ──────── Path to V4L2 device. Default: %s.\n\n", dev->path);
printf(" -i|--input <N> -- Input channel. Default: %u.\n\n", dev->input); printf(" -i|--input <N> ───────────────── Input channel. Default: %u.\n\n", dev->input);
printf(" -x|--width <N> -- Initial image width. Default: %u.\n\n", dev->width); printf(" -x|--width <N> ───────────────── Initial image width. Default: %u.\n\n", dev->width);
printf(" -y|--height <N> -- Initial image height. Default: %u.\n\n", dev->height); printf(" -y|--height <N> ──────────────── Initial image height. Default: %u.\n\n", dev->height);
printf(" -m|--format <fmt> -- Image format.\n"); printf(" -m|--format <fmt> ────────────── Image format.\n");
printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR); printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR);
printf(" -a|--tv-standard <std> -- Force TV standard.\n"); printf(" -a|--tv-standard <std> ───────── Force TV standard.\n");
printf(" Available: %s; default: disabled.\n\n", STANDARDS_STR); printf(" Available: %s; default: disabled.\n\n", STANDARDS_STR);
printf(" -f|--desired-fps <N> -- Desired FPS. Default: maximum as possible.\n\n"); printf(" -f|--desired-fps <N> ─────────── Desired FPS. Default: maximum as possible.\n\n");
printf(" -z|--min-frame-size <N> -- Drop frames smaller then this limit.\n"); printf(" -z|--min-frame-size <N> ──────── Drop frames smaller then this limit.\n");
printf(" Useful if the device produces small-sized garbage frames.\n\n"); printf(" Useful if the device produces small-sized garbage frames.\n\n");
printf(" -n|--persistent -- Don't re-initialize device on timeout. Default: disabled.\n\n"); printf(" -n|--persistent ──────────────── Don't re-initialize device on timeout. Default: disabled.\n\n");
printf(" -t|--dv-timings -- Enable DV timings queriyng and events processing.\n"); printf(" -t|--dv-timings ──────────────── Enable DV timings queriyng and events processing.\n");
printf(" Supports automatic resolution changing. Default: disabled.\n\n"); printf(" Supports automatic resolution changing. Default: disabled.\n\n");
printf(" -b|--buffers <N> -- The number of buffers to receive data from the device.\n"); printf(" -b|--buffers <N> ─────────────── The number of buffers to receive data from the device.\n");
printf(" Each buffer may processed using an intermediate thread.\n"); printf(" Each buffer may processed using an intermediate thread.\n");
printf(" Default: %u (number of CPU cores + 1)\n\n", dev->n_buffers); printf(" Default: %u (the number of CPU cores (but not more 4) + 1)\n\n", dev->n_buffers);
printf(" -w|--workers <N> -- The number of compressing threads. Default: %u (== --buffers).\n\n", dev->n_workers); printf(" -w|--workers <N> ─────────────── The number of worker threads. Default: %u (== --buffers).\n\n", dev->n_workers);
printf(" -q|--quality <N> -- Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality); printf(" -q|--quality <N> ─────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality);
printf(" -c|--encoder <type> -- Use specified encoder. It may affects to workers number.\n"); printf(" -c|--encoder <type> ──────────── Use specified encoder. It may affects to workers number.\n");
printf(" -- Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR); printf(" Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
printf(" --device-timeout <seconds> -- Timeout for device querying. Default: %u\n\n", dev->timeout); printf(" --device-timeout <seconds> ───── Timeout for device querying. Default: %u\n\n", dev->timeout);
printf(" --device-error-delay <seconds> -- Delay before trying to connect to the device again\n"); printf(" --device-error-delay <seconds> Delay before trying to connect to the device again\n");
printf(" after timeout. Default: %u\n\n", dev->error_delay); printf(" after timeout. Default: %u\n\n", dev->error_delay);
printf("Image control options:\n"); printf("Image control options:\n");
printf("---------------\n"); printf("══════════════════════\n");
printf(" --brightness <N> -- Set brightness. Default: no change.\n\n"); printf(" --brightness <N> ───────────── Set brightness. Default: no change.\n\n");
printf(" --brightness-auto -- Enable automatic brightness control. Default: no change.\n\n"); printf(" --brightness-auto ──────────── Enable automatic brightness control. Default: no change.\n\n");
printf(" --contrast <N> -- Set contrast. Default: no change.\n\n"); printf(" --contrast <N> ─────────────── Set contrast. Default: no change.\n\n");
printf(" --saturation <N> -- Set saturation. Default: no change.\n\n"); printf(" --saturation <N> ───────────── Set saturation. Default: no change.\n\n");
printf(" --hue <N> -- Set hue. Default: no change.\n\n"); printf(" --hue <N> ──────────────────── Set hue. Default: no change.\n\n");
printf(" --hue-auto -- Enable automatic hue control. Default: no change.\n\n"); printf(" --hue-auto ─────────────────── Enable automatic hue control. Default: no change.\n\n");
printf(" --gamma <N> -- Set gamma. Default: no change.\n\n"); printf(" --gamma <N> ────────────────── Set gamma. Default: no change.\n\n");
printf(" --sharpness <N> -- Set sharpness. Default: no change.\n\n"); printf(" --sharpness <N> ────────────── Set sharpness. Default: no change.\n\n");
printf(" --backlight-compensation <N> -- Set backlight compensation. Default: no change.\n\n"); printf(" --backlight-compensation <N> Set backlight compensation. Default: no change.\n\n");
printf(" --white-balance <N> -- Set white balance. Default: no change.\n\n"); printf(" --white-balance <N> ────────── Set white balance. Default: no change.\n\n");
printf(" --white-balance-auto -- Enable automatic white balance control. Default: no change.\n\n"); printf(" --white-balance-auto ───────── Enable automatic white balance control. Default: no change.\n\n");
printf(" --gain <N> -- Set gain. Default: no change.\n\n"); printf(" --gain <N> ─────────────────── Set gain. Default: no change.\n\n");
printf(" --gain-auto -- Enable automatic gain control. Default: no change.\n\n"); printf(" --gain-auto ────────────────── Enable automatic gain control. Default: no change.\n\n");
printf("HTTP server options:\n"); printf("HTTP server options:\n");
printf("--------------------\n"); printf("════════════════════\n");
printf(" -s|--host <address> -- Listen on Hostname or IP. Default: %s\n\n", server->host); printf(" -s|--host <address> ──────── Listen on Hostname or IP. Default: %s\n\n", server->host);
printf(" -p|--port <N> -- Bind to this TCP port. Default: %u\n\n", server->port); printf(" -p|--port <N> ────────────── Bind to this TCP port. Default: %u\n\n", server->port);
printf(" -u|--unix <path> -- Bind to UNIX domain socket. Default: disabled\n\n"); printf(" -u|--unix <path> ─────────── Bind to UNIX domain socket. Default: disabled\n\n");
printf(" -r|--unix-rm -- Try to remove old UNIX socket file before binding. Default: disabled\n\n"); printf(" -r|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled\n\n");
printf(" -o|--unix-mode <mode> -- Set UNIX socket file permissions (like 777). Default: disabled\n\n"); printf(" -o|--unix-mode <mode> ────── Set UNIX socket file permissions (like 777). Default: disabled\n\n");
printf(" -e|--drop-same-frames <N> -- Don't send same frames to clients, but no more than specified number.\n"); printf(" -e|--drop-same-frames <N> ── Don't send same frames to clients, but no more than specified number.\n");
printf(" It can significantly reduce the outgoing traffic, but will increase\n"); printf(" It can significantly reduce the outgoing traffic, but will increase\n");
printf(" the CPU loading. Don't use this option with analog signal sources\n"); printf(" the CPU loading. Don't use this option with analog signal sources\n");
printf(" or webcams, it's useless. Default: disabled.\n\n"); printf(" or webcams, it's useless. Default: disabled.\n\n");
printf(" --slowdown -- Slowdown capturing to 1 FPS or less when no stream clients connected.\n"); printf(" -l|--slowdown ────────────── Slowdown capturing to 1 FPS or less when no stream clients connected.\n");
printf(" Useful to reduce CPU cosumption. Default: disabled.\n\n"); printf(" Useful to reduce CPU cosumption. Default: disabled.\n\n");
printf(" --fake-width <N> -- Override image width for /state. Default: disabled\n\n"); printf(" --fake-width <N> ─────────── Override image width for /state. Default: disabled\n\n");
printf(" --fake-height <N> -- Override image height for /state. Default: disabled.\n\n"); printf(" --fake-height <N> ────────── Override image height for /state. Default: disabled.\n\n");
printf(" --server-timeout <seconds> -- Timeout for client connections. Default: %u\n\n", server->timeout); printf(" --server-timeout <seconds> Timeout for client connections. Default: %u\n\n", server->timeout);
printf("Misc options:\n"); printf("Misc options:\n");
printf("-------------\n"); printf("═════════════\n");
printf(" --log-level <N> -- Verbosity level of messages from 0 (info) to 3 (debug).\n"); printf(" --log-level <N> Verbosity level of messages from 0 (info) to 3 (debug).\n");
printf(" Enabling debugging messages can slow down the program.\n"); printf(" Enabling debugging messages can slow down the program.\n");
printf(" Available levels: 0=info, 1=performance, 2=verbose, 3=debug.\n"); printf(" Available levels: 0=info, 1=performance, 2=verbose, 3=debug.\n");
printf(" Default: %u.\n\n", log_level); printf(" Default: %u.\n\n", log_level);
printf(" --perf -- Enable performance messages (same as --log-level=1). Default: disabled.\n\n"); printf(" --perf ────────── Enable performance messages (same as --log-level=1). Default: disabled.\n\n");
printf(" --verbose -- Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n\n"); printf(" --verbose ─────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n\n");
printf(" --debug -- Enable debug messages and lower (same as --log-level=3). Default: disabled.\n\n"); printf(" --debug ───────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n\n");
printf(" -h|--help -- Print this messages and exit.\n\n"); printf(" -h|--help ─────── Print this text and exit.\n\n");
printf(" -v|--version ──── Print version and exit.\n\n");
} }
static int _parse_options(int argc, char *argv[], struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) { static int _parse_options(int argc, char *argv[], struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
@@ -284,7 +285,7 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
case 5002: OPT_SET(log_level, LOG_LEVEL_DEBUG); case 5002: OPT_SET(log_level, LOG_LEVEL_DEBUG);
case 5010: OPT_UNSIGNED(log_level, "--log-level", 0, 3); case 5010: OPT_UNSIGNED(log_level, "--log-level", 0, 3);
case 'h': _help(dev, encoder, server); return 1; case 'h': _help(dev, encoder, server); return 1;
case 6000: _version(true); return 1; case 'v': _version(true); return 1;
case 0: break; case 0: break;
default: _help(dev, encoder, server); return -1; default: _help(dev, encoder, server); return -1;
} }

View File

@@ -49,11 +49,6 @@ static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t
static void *_stream_worker_thread(void *v_worker); static void *_stream_worker_thread(void *v_worker);
static void _stream_destroy_workers(struct stream_t *stream, struct workers_pool_t *pool); static void _stream_destroy_workers(struct stream_t *stream, struct workers_pool_t *pool);
static int _stream_control(struct device_t *dev, bool enable);
static int _stream_grab_buffer(struct device_t *dev, struct v4l2_buffer *buf_info);
static int _stream_release_buffer(struct device_t *dev, struct v4l2_buffer *buf_info);
static int _stream_handle_event(struct device_t *dev);
struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) { struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) {
struct process_t *proc; struct process_t *proc;
@@ -191,22 +186,23 @@ void stream_loop(struct stream_t *stream) {
if (FD_ISSET(stream->dev->run->fd, &read_fds)) { if (FD_ISSET(stream->dev->run->fd, &read_fds)) {
LOG_DEBUG("Frame is ready"); LOG_DEBUG("Frame is ready");
struct v4l2_buffer buf_info; int buf_index;
long double now = get_now_monotonic(); long double now = get_now_monotonic();
long long now_second = floor_ms(now); long long now_second = floor_ms(now);
if (_stream_grab_buffer(stream->dev, &buf_info) < 0) { if ((buf_index = device_grab_buffer(stream->dev)) < 0) {
break; break;
} }
stream->dev->run->pictures[buf_info.index].grab_time = now; stream->dev->run->pictures[buf_index].grab_time = now;
// Workaround for broken, corrupted frames: // Workaround for broken, corrupted frames:
// Under low light conditions corrupted frames may get captured. // Under low light conditions corrupted frames may get captured.
// The good thing is such frames are quite small compared to the regular pictures. // The good thing is such frames are quite small compared to the regular pictures.
// For example a VGA (640x480) webcam picture is normally >= 8kByte large, // For example a VGA (640x480) webcam picture is normally >= 8kByte large,
// corrupted frames are smaller. // corrupted frames are smaller.
if (buf_info.bytesused < stream->dev->min_frame_size) { if (stream->dev->run->hw_buffers[buf_index].used < stream->dev->min_frame_size) {
LOG_DEBUG("Dropping too small frame sized %u bytes, assuming it as broken", buf_info.bytesused); LOG_DEBUG("Dropping too small frame sized %zu bytes, assuming it as broken",
stream->dev->run->hw_buffers[buf_index].used);
goto pass_frame; goto pass_frame;
} }
@@ -234,8 +230,7 @@ void stream_loop(struct stream_t *stream) {
# define FREE_WORKER(_next) pool.workers[free_worker_number]._next # define FREE_WORKER(_next) pool.workers[free_worker_number]._next
LOG_DEBUG("Grabbed a new frame to buffer %u", buf_info.index); LOG_DEBUG("Grabbed a new frame to buffer %u", buf_index);
FREE_WORKER(buf_info) = buf_info;
if (!oldest_worker) { if (!oldest_worker) {
oldest_worker = &pool.workers[free_worker_number]; oldest_worker = &pool.workers[free_worker_number];
@@ -254,7 +249,7 @@ void stream_loop(struct stream_t *stream) {
last_worker->order_next = NULL; last_worker->order_next = NULL;
A_MUTEX_LOCK(&FREE_WORKER(has_job_mutex)); A_MUTEX_LOCK(&FREE_WORKER(has_job_mutex));
FREE_WORKER(buf_index) = buf_info.index; FREE_WORKER(buf_index) = buf_index;
atomic_store(&FREE_WORKER(has_job), true); atomic_store(&FREE_WORKER(has_job), true);
A_MUTEX_UNLOCK(&FREE_WORKER(has_job_mutex)); A_MUTEX_UNLOCK(&FREE_WORKER(has_job_mutex));
A_COND_SIGNAL(&FREE_WORKER(has_job_cond)); A_COND_SIGNAL(&FREE_WORKER(has_job_cond));
@@ -269,7 +264,7 @@ void stream_loop(struct stream_t *stream) {
pass_frame: pass_frame:
if (_stream_release_buffer(stream->dev, &buf_info) < 0) { if (device_release_buffer(stream->dev, buf_index) < 0) {
break; break;
} }
} }
@@ -283,7 +278,7 @@ void stream_loop(struct stream_t *stream) {
if (FD_ISSET(stream->dev->run->fd, &error_fds)) { if (FD_ISSET(stream->dev->run->fd, &error_fds)) {
LOG_INFO("Got V4L2 event"); LOG_INFO("Got V4L2 event");
if (_stream_handle_event(stream->dev) < 0) { if (device_consume_event(stream->dev) < 0) {
break; break;
} }
} }
@@ -291,7 +286,7 @@ void stream_loop(struct stream_t *stream) {
} }
A_MUTEX_LOCK(&stream->mutex); A_MUTEX_LOCK(&stream->mutex);
stream->picture.size = 0; // On stream offline stream->picture.used = 0; // On stream offline
free(stream->picture.data); free(stream->picture.data);
stream->width = 0; stream->width = 0;
stream->height = 0; stream->height = 0;
@@ -300,7 +295,7 @@ void stream_loop(struct stream_t *stream) {
} }
_stream_destroy_workers(stream, &pool); _stream_destroy_workers(stream, &pool);
_stream_control(stream->dev, false); device_switch_capturing(stream->dev, false);
device_close(stream->dev); device_close(stream->dev);
} }
@@ -317,10 +312,10 @@ static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index)
A_MUTEX_LOCK(&stream->mutex); A_MUTEX_LOCK(&stream->mutex);
stream->picture.size = PICTURE(size); stream->picture.used = PICTURE(used);
stream->picture.allocated = PICTURE(allocated); stream->picture.allocated = PICTURE(allocated);
memcpy(stream->picture.data, PICTURE(data), stream->picture.size * sizeof(*stream->picture.data)); memcpy(stream->picture.data, PICTURE(data), stream->picture.used * sizeof(*stream->picture.data));
stream->picture.grab_time = PICTURE(grab_time); stream->picture.grab_time = PICTURE(grab_time);
stream->picture.encode_begin_time = PICTURE(encode_begin_time); stream->picture.encode_begin_time = PICTURE(encode_begin_time);
@@ -384,13 +379,13 @@ static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
SEP_INFO('='); SEP_INFO('=');
_stream_destroy_workers(stream, pool); _stream_destroy_workers(stream, pool);
_stream_control(stream->dev, false); device_switch_capturing(stream->dev, false);
device_close(stream->dev); device_close(stream->dev);
if (device_open(stream->dev) < 0) { if (device_open(stream->dev) < 0) {
goto error; goto error;
} }
if (_stream_control(stream->dev, true) < 0) { if (device_switch_capturing(stream->dev, true) < 0) {
goto error; goto error;
} }
@@ -462,7 +457,7 @@ static void *_stream_worker_thread(void *v_worker) {
} }
PICTURE(encode_end_time) = get_now_monotonic(); PICTURE(encode_end_time) = get_now_monotonic();
if (_stream_release_buffer(worker->dev, &worker->buf_info) == 0) { if (device_release_buffer(worker->dev, worker->buf_index) == 0) {
worker->job_start_time = PICTURE(encode_begin_time); worker->job_start_time = PICTURE(encode_begin_time);
atomic_store(&worker->has_job, false); atomic_store(&worker->has_job, false);
@@ -473,7 +468,7 @@ static void *_stream_worker_thread(void *v_worker) {
A_MUTEX_UNLOCK(&worker->last_comp_time_mutex); A_MUTEX_UNLOCK(&worker->last_comp_time_mutex);
LOG_VERBOSE("Compressed JPEG size=%zu; time=%0.3Lf; worker=%u; buffer=%u", LOG_VERBOSE("Compressed JPEG size=%zu; time=%0.3Lf; worker=%u; buffer=%u",
PICTURE(size), last_comp_time, worker->number, worker->buf_index); PICTURE(used), last_comp_time, worker->number, worker->buf_index);
} else { } else {
worker->job_failed = true; worker->job_failed = true;
atomic_store(&worker->has_job, false); atomic_store(&worker->has_job, false);
@@ -520,68 +515,3 @@ static void _stream_destroy_workers(struct stream_t *stream, struct workers_pool
pool->free_workers = 0; pool->free_workers = 0;
pool->workers = NULL; pool->workers = NULL;
} }
static int _stream_control(struct device_t *dev, bool enable) {
if (enable != dev->run->capturing) {
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
LOG_DEBUG("Calling ioctl(%s) ...", (enable ? "VIDIOC_STREAMON" : "VIDIOC_STREAMOFF"));
if (xioctl(dev->run->fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
LOG_PERROR("Unable to %s capturing", (enable ? "start" : "stop"));
if (enable) {
return -1;
}
}
dev->run->capturing = enable;
LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
}
return 0;
}
static int _stream_grab_buffer(struct device_t *dev, struct v4l2_buffer *buf_info) {
MEMSET_ZERO_PTR(buf_info);
buf_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf_info->memory = V4L2_MEMORY_MMAP;
LOG_DEBUG("Calling ioctl(VIDIOC_DQBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_DQBUF, buf_info) < 0) {
LOG_PERROR("Unable to dequeue buffer");
return -1;
}
LOG_DEBUG("Got a new frame in buffer index=%u; bytesused=%u", buf_info->index, buf_info->bytesused);
if (buf_info->index >= dev->run->n_buffers) {
LOG_ERROR("Got invalid buffer index=%u; nbuffers=%u", buf_info->index, dev->run->n_buffers);
return -1;
}
return 0;
}
static int _stream_release_buffer(struct device_t *dev, struct v4l2_buffer *buf_info) {
LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_QBUF, buf_info) < 0) {
LOG_PERROR("Unable to requeue buffer");
return -1;
}
return 0;
}
static int _stream_handle_event(struct device_t *dev) {
struct v4l2_event event;
LOG_DEBUG("Calling ioctl(VIDIOC_DQEVENT) ...");
if (!xioctl(dev->run->fd, VIDIOC_DQEVENT, &event)) {
switch (event.type) {
case V4L2_EVENT_SOURCE_CHANGE:
LOG_INFO("Got V4L2_EVENT_SOURCE_CHANGE: source changed");
return -1;
case V4L2_EVENT_EOS:
LOG_INFO("Got V4L2_EVENT_EOS: end of stream (ignored)");
return 0;
}
} else {
LOG_PERROR("Got some V4L2 device event, but where is it? ");
}
return 0;
}

View File

@@ -42,7 +42,6 @@ struct worker_t {
pthread_mutex_t has_job_mutex; pthread_mutex_t has_job_mutex;
int buf_index; int buf_index;
struct v4l2_buffer buf_info;
atomic_bool has_job; atomic_bool has_job;
bool job_failed; bool job_failed;
long double job_start_time; long double job_start_time;

View File

@@ -25,12 +25,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <math.h> #include <math.h>
#include <pthread.h> #include <pthread.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/syscall.h> #include <sys/syscall.h>
@@ -51,7 +53,6 @@
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest))))) #define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
#define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest))))) #define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
#define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj)) #define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj))
#define MEMSET_ZERO_PTR(_ptr) memset(_ptr, 0, sizeof(*(_ptr)))
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0])) #define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
@@ -63,12 +64,16 @@ INLINE char *bool_to_string(bool flag) {
return (flag ? "true" : "false"); return (flag ? "true" : "false");
} }
INLINE unsigned min_u(unsigned a, unsigned b) {
return (a < b ? a : b);
}
INLINE unsigned max_u(unsigned a, unsigned b) { INLINE unsigned max_u(unsigned a, unsigned b) {
return (a > b ? a : b); return (a > b ? a : b);
} }
INLINE long long floor_ms(long double now) { INLINE long long floor_ms(long double now) {
return (long long) now - (now < (long long) now); // floor() return (long long)now - (now < (long long)now); // floor()
} }
INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) { INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
@@ -99,3 +104,7 @@ INLINE long double get_now_real(void) {
get_now(CLOCK_REALTIME, &sec, &msec); get_now(CLOCK_REALTIME, &sec, &msec);
return (long double)sec + ((long double)msec) / 1000; return (long double)sec + ((long double)msec) / 1000;
} }
INLINE pid_t get_thread_id(void) {
return syscall(SYS_gettid);
}

View File

@@ -30,7 +30,9 @@
#include "logging.h" #include "logging.h"
#define XIOCTL_RETRIES 4 #ifndef XIOCTL_RETRIES
# define XIOCTL_RETRIES 4
#endif
INLINE int xioctl(int fd, int request, void *arg) { INLINE int xioctl(int fd, int request, void *arg) {