mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-19 08:16:31 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
348a6e4a8f | ||
|
|
ba3300ddde | ||
|
|
09526884f4 | ||
|
|
160a0e8d29 | ||
|
|
9dddd0075e | ||
|
|
0e0e3939b2 | ||
|
|
9c8d87412e | ||
|
|
3666d92819 | ||
|
|
9388d4dd22 | ||
|
|
6cf4924e05 | ||
|
|
713e3751b4 | ||
|
|
8e61d7c55e | ||
|
|
4e42c42bae | ||
|
|
b53e3edef1 |
@@ -1,7 +1,7 @@
|
||||
[bumpversion]
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 0.40
|
||||
current_version = 0.44
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
||||
serialize =
|
||||
{major}.{minor}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,4 +5,5 @@
|
||||
/ustreamer-*.pkg.tar.xz
|
||||
/vgcore.*
|
||||
/ustreamer
|
||||
/*.sock
|
||||
*.o
|
||||
|
||||
2
Makefile
2
Makefile
@@ -59,5 +59,5 @@ push:
|
||||
|
||||
clean-all: clean
|
||||
clean:
|
||||
rm -f src/*.o src/{jpeg,omx}/*.o vgcore.* $(PROG)
|
||||
rm -f src/*.o src/{jpeg,omx}/*.o vgcore.* *.sock $(PROG)
|
||||
rm -rf pkg src/$(PROG)-* src/v*.tar.gz v*.tar.gz $(PROG)-*.pkg.tar.xz
|
||||
|
||||
2
PKGBUILD
2
PKGBUILD
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
pkgname=ustreamer
|
||||
pkgver=0.40
|
||||
pkgver=0.44
|
||||
pkgrel=1
|
||||
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
||||
url="https://github.com/pi-kvm/ustreamer"
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
|----------|---------------|-------------------|
|
||||
| Multithreaded JPEG encoding |  Yes |  No |
|
||||
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi |  Yes |  No |
|
||||
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast<sup>1</sup> |
|
||||
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast <sup>1</sup> |
|
||||
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal |  Yes |  Partially yes <sup>1</sup> |
|
||||
| Option to skip frames when streaming<br>static images by HTTP to save traffic |  Yes <sup>2</sup> |  No |
|
||||
| Streaming via UNIX domain socket |  Yes |  No |
|
||||
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters |  Yes |  No |
|
||||
| Supported input devices |  YUYV, UYVY,<br>RGB565, ~~MJPG~~ <sup>3</sup> |  YUYV, UYVY,<br>RGB565, MJPG |
|
||||
| Access to webcam controls (focus, servos)<br>and settings such as brightness via HTTP |  No |  Yes |
|
||||
@@ -58,7 +59,7 @@ The recommended way of running µStreamer with [Auvidea B101](https://www.raspbe
|
||||
$ ./ustreamer \
|
||||
--format=uyvy \ # Device input format
|
||||
--encoder=omx \ # Hardware encoding with OpenMAX
|
||||
--dv-timings \ # use DV-timings
|
||||
--dv-timings \ # Use DV-timings
|
||||
--quality=20 \ # OpenMAX has a non-linear quality scale
|
||||
--drop-same-frames=30 # Save that traffic
|
||||
```
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
| Поведение при физическом отключении устройства<br>от сервера во время работы |  Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова |  Прерывает трансляцию <sup>1</sup> |
|
||||
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности изменения <br>параметров разрешения трансляции на лету<br>по сигналу источника (устройства видеозахвата) |  Есть |  Условно есть <sup>1</sup> |
|
||||
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика |  Есть <sup>2</sup> |  Нет |
|
||||
| Стрим через UNIX domain socket |  Есть |  Нет |
|
||||
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP |  Есть |  Нет |
|
||||
| Поддерживаемые входные форматы устройств |  YUYV, UYVY,<br>RGB565, ~~MJPG~~ <sup>3</sup> |  YUYV, UYVY,<br>RGB565, MJPG |
|
||||
| Поддержка контролов веб-камер (фокус,<br> движение сервами) и всяких настроек,<br> типа яркости, через HTTP |  Нет |  Есть |
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VERSION "0.40"
|
||||
#define VERSION "0.44"
|
||||
|
||||
57
src/device.c
57
src/device.c
@@ -26,6 +26,7 @@
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <linux/videodev2.h>
|
||||
@@ -63,6 +64,7 @@ static int _device_open_format(struct device_t *dev);
|
||||
static void _device_open_alloc_picbufs(struct device_t *dev);
|
||||
static int _device_open_mmap(struct device_t *dev);
|
||||
static int _device_open_queue_buffers(struct device_t *dev);
|
||||
static int _device_apply_resolution(struct device_t *dev, const unsigned width, const unsigned height);
|
||||
|
||||
static const char *_format_to_string_auto(char *buf, const size_t size, const unsigned format);
|
||||
static const char *_format_to_string_null(const unsigned format);
|
||||
@@ -161,7 +163,7 @@ void device_close(struct device_t *dev) {
|
||||
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
|
||||
if (dev->run->hw_buffers[index].start != MAP_FAILED) {
|
||||
if (munmap(dev->run->hw_buffers[index].start, dev->run->hw_buffers[index].length) < 0) {
|
||||
LOG_PERROR("Can't unmap device buffer %d", index);
|
||||
LOG_PERROR("Can't unmap device buffer %u", index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,6 +224,7 @@ static int _device_open_check_cap(struct device_t *dev) {
|
||||
}
|
||||
|
||||
static int _device_open_dv_timings(struct device_t *dev) {
|
||||
_device_apply_resolution(dev, dev->width, dev->height);
|
||||
if (dev->dv_timings) {
|
||||
LOG_DEBUG("Using DV-timings");
|
||||
|
||||
@@ -239,10 +242,6 @@ static int _device_open_dv_timings(struct device_t *dev) {
|
||||
LOG_PERROR("Can't subscribe to V4L2_EVENT_SOURCE_CHANGE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
dev->run->width = dev->width;
|
||||
dev->run->height = dev->height;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -255,7 +254,7 @@ static int _device_apply_dv_timings(struct device_t *dev) {
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERY_DV_TIMINGS) ...");
|
||||
if (xioctl(dev->run->fd, VIDIOC_QUERY_DV_TIMINGS, &dv_timings) == 0) {
|
||||
LOG_INFO(
|
||||
"Got new DV timings: resolution=%dx%d; pixclk=%llu",
|
||||
"Got new DV timings: resolution=%ux%u; pixclk=%llu",
|
||||
dv_timings.bt.width,
|
||||
dv_timings.bt.height,
|
||||
dv_timings.bt.pixelclock
|
||||
@@ -267,8 +266,9 @@ static int _device_apply_dv_timings(struct device_t *dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->run->width = dv_timings.bt.width;
|
||||
dev->run->height = dv_timings.bt.height;
|
||||
if (_device_apply_resolution(dev, dv_timings.bt.width, dv_timings.bt.height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYSTD) ...");
|
||||
@@ -299,7 +299,7 @@ static int _device_open_format(struct device_t *dev) {
|
||||
char format_str[8];
|
||||
|
||||
LOG_PERROR(
|
||||
"Unable to set format=%s; resolution=%dx%d",
|
||||
"Unable to set format=%s; resolution=%ux%u",
|
||||
_format_to_string_auto(format_str, 8, dev->format),
|
||||
dev->run->width,
|
||||
dev->run->height
|
||||
@@ -309,11 +309,12 @@ static int _device_open_format(struct device_t *dev) {
|
||||
|
||||
// Check resolution
|
||||
if (fmt.fmt.pix.width != dev->run->width || fmt.fmt.pix.height != dev->run->height) {
|
||||
LOG_ERROR("Requested resolution=%dx%d is unavailable", dev->run->width, dev->run->height);
|
||||
LOG_ERROR("Requested resolution=%ux%u is unavailable", dev->run->width, dev->run->height);
|
||||
}
|
||||
dev->run->width = fmt.fmt.pix.width;
|
||||
dev->run->height = fmt.fmt.pix.height;
|
||||
LOG_INFO("Using resolution: %dx%d", dev->run->width, dev->run->height);
|
||||
if (_device_apply_resolution(dev, fmt.fmt.pix.width, fmt.fmt.pix.height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO("Using resolution: %ux%u", dev->run->width, dev->run->height);
|
||||
|
||||
// Check format
|
||||
if (fmt.fmt.pix.pixelformat != dev->format) {
|
||||
@@ -357,10 +358,10 @@ static int _device_open_mmap(struct device_t *dev) {
|
||||
}
|
||||
|
||||
if (req.count < 1) {
|
||||
LOG_ERROR("Insufficient buffer memory: %d", req.count);
|
||||
LOG_ERROR("Insufficient buffer memory: %u", req.count);
|
||||
return -1;
|
||||
} else {
|
||||
LOG_INFO("Requested %d HW buffers, got %d", dev->n_buffers, req.count);
|
||||
LOG_INFO("Requested %u HW buffers, got %u", dev->n_buffers, req.count);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Allocating HW buffers ...");
|
||||
@@ -374,17 +375,17 @@ static int _device_open_mmap(struct device_t *dev) {
|
||||
buf_info.memory = V4L2_MEMORY_MMAP;
|
||||
buf_info.index = dev->run->n_buffers;
|
||||
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYBUF) for device buffer %d ...", dev->run->n_buffers);
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYBUF) for device buffer %u ...", dev->run->n_buffers);
|
||||
if (xioctl(dev->run->fd, VIDIOC_QUERYBUF, &buf_info) < 0) {
|
||||
LOG_PERROR("Can't VIDIOC_QUERYBUF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Mapping device buffer %d ...", dev->run->n_buffers);
|
||||
LOG_DEBUG("Mapping device buffer %u ...", dev->run->n_buffers);
|
||||
dev->run->hw_buffers[dev->run->n_buffers].length = buf_info.length;
|
||||
dev->run->hw_buffers[dev->run->n_buffers].start = mmap(NULL, buf_info.length, PROT_READ|PROT_WRITE, MAP_SHARED, dev->run->fd, buf_info.m.offset);
|
||||
if (dev->run->hw_buffers[dev->run->n_buffers].start == MAP_FAILED) {
|
||||
LOG_PERROR("Can't map device buffer %d", dev->run->n_buffers);
|
||||
LOG_PERROR("Can't map device buffer %u", dev->run->n_buffers);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +401,7 @@ static int _device_open_queue_buffers(struct device_t *dev) {
|
||||
buf_info.memory = V4L2_MEMORY_MMAP;
|
||||
buf_info.index = index;
|
||||
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) for buffer %d ...", index);
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) for buffer %u ...", index);
|
||||
if (xioctl(dev->run->fd, VIDIOC_QBUF, &buf_info) < 0) {
|
||||
LOG_PERROR("Can't VIDIOC_QBUF");
|
||||
return -1;
|
||||
@@ -415,12 +416,28 @@ static void _device_open_alloc_picbufs(struct device_t *dev) {
|
||||
|
||||
dev->run->max_picture_size = ((dev->run->width * dev->run->height) << 1) * 2;
|
||||
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
|
||||
LOG_DEBUG("Allocating picture buffer %d sized %lu bytes... ", index, dev->run->max_picture_size);
|
||||
LOG_DEBUG("Allocating picture buffer %u sized %lu bytes... ", index, dev->run->max_picture_size);
|
||||
A_CALLOC(dev->run->pictures[index].data, dev->run->max_picture_size);
|
||||
dev->run->pictures[index].allocated = dev->run->max_picture_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int _device_apply_resolution(struct device_t *dev, const unsigned width, const unsigned height) {
|
||||
// Тут VIDEO_MIN_* не используются из-за странностей минимального разрешения при отсутствии сигнала
|
||||
// у некоторых устройств, например Auvidea B101
|
||||
if (
|
||||
width == 0 || width > VIDEO_MAX_WIDTH
|
||||
|| height == 0 || height > VIDEO_MAX_HEIGHT
|
||||
) {
|
||||
LOG_ERROR("Requested forbidden resolution=%ux%u: min=1x1; max=%ux%u",
|
||||
width, height, VIDEO_MAX_WIDTH, VIDEO_MAX_HEIGHT);
|
||||
return -1;
|
||||
}
|
||||
dev->run->width = width;
|
||||
dev->run->height = height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *_format_to_string_auto(char *buf, const size_t size, const unsigned format) {
|
||||
assert(size >= 8);
|
||||
buf[0] = format & 0x7f;
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
|
||||
#define VIDEO_MIN_WIDTH 320
|
||||
#define VIDEO_MAX_WIDTH 1920
|
||||
|
||||
#define VIDEO_MIN_HEIGHT 180
|
||||
#define VIDEO_MAX_HEIGHT 1200
|
||||
|
||||
#define STANDARD_UNKNOWN V4L2_STD_UNKNOWN
|
||||
#define STANDARDS_STR "UNKNOWN, PAL, NTSC, SECAM"
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
|
||||
LOG_DEBUG("Initializing encoder ...");
|
||||
}
|
||||
|
||||
LOG_INFO("Using JPEG quality: %d%%", encoder->quality);
|
||||
LOG_INFO("Using JPEG quality: %u%%", encoder->quality);
|
||||
|
||||
# ifdef OMX_ENCODER
|
||||
if (encoder->type == ENCODER_TYPE_OMX) {
|
||||
@@ -129,7 +129,7 @@ void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
|
||||
# ifdef OMX_ENCODER
|
||||
if (encoder->type == ENCODER_TYPE_OMX) {
|
||||
for (unsigned index = 0; index < encoder->n_omxs; ++index) {
|
||||
if (omx_encoder_prepare_live(encoder->omxs[index], dev, encoder->quality, encoder->omx_use_ijg) < 0) {
|
||||
if (omx_encoder_prepare_live(encoder->omxs[index], dev, encoder->quality) < 0) {
|
||||
goto use_fallback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ struct encoder_t {
|
||||
enum encoder_type_t type;
|
||||
unsigned quality;
|
||||
#ifdef OMX_ENCODER
|
||||
bool omx_use_ijg;
|
||||
unsigned n_omxs;
|
||||
struct omx_encoder_t **omxs;
|
||||
#endif
|
||||
|
||||
61
src/http.c
61
src/http.c
@@ -23,8 +23,14 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <event2/thread.h>
|
||||
#include <event2/http.h>
|
||||
@@ -105,6 +111,9 @@ void http_server_destroy(struct http_server_t *server) {
|
||||
}
|
||||
|
||||
evhttp_free(server->run->http);
|
||||
if (server->run->unix_fd) {
|
||||
close(server->run->unix_fd);
|
||||
}
|
||||
event_base_free(server->run->base);
|
||||
libevent_global_shutdown();
|
||||
|
||||
@@ -128,15 +137,57 @@ int http_server_listen(struct http_server_t *server) {
|
||||
|
||||
server->run->drop_same_frames_blank = max_u(server->drop_same_frames, server->run->drop_same_frames_blank);
|
||||
|
||||
LOG_DEBUG("Binding HTTP to [%s]:%d ...", server->host, server->port);
|
||||
evhttp_set_timeout(server->run->http, server->timeout);
|
||||
|
||||
if (evhttp_bind_socket(server->run->http, server->host, server->port) < 0) {
|
||||
LOG_PERROR("Can't listen HTTP on [%s]:%d", server->host, server->port)
|
||||
return -1;
|
||||
if (server->unix_path) {
|
||||
struct sockaddr_un unix_addr;
|
||||
int unix_fd_flags;
|
||||
|
||||
LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
|
||||
|
||||
assert((server->run->unix_fd = socket(AF_UNIX, SOCK_STREAM, 0)));
|
||||
assert((unix_fd_flags = fcntl(server->run->unix_fd, F_GETFL)) >= 0);
|
||||
unix_fd_flags |= O_NONBLOCK;
|
||||
assert(fcntl(server->run->unix_fd, F_SETFL, unix_fd_flags) >= 0);
|
||||
|
||||
strncpy(unix_addr.sun_path, server->unix_path, 107);
|
||||
unix_addr.sun_path[107] = '\0';
|
||||
unix_addr.sun_family = AF_UNIX;
|
||||
|
||||
if (server->unix_rm && unlink(server->unix_path) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_PERROR("Can't remove old UNIX socket '%s'", server->unix_path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (bind(server->run->unix_fd, (struct sockaddr *)&unix_addr, sizeof(struct sockaddr_un)) < 0) {
|
||||
LOG_PERROR("Can't bind HTTP to UNIX socket '%s'", server->unix_path);
|
||||
return -1;
|
||||
}
|
||||
if (server->unix_mode && chmod(server->unix_path, server->unix_mode) < 0) {
|
||||
LOG_PERROR("Can't set permissions %o to UNIX socket '%s'", server->unix_mode, server->unix_path);
|
||||
return -1;
|
||||
}
|
||||
if (listen(server->run->unix_fd, 128) < 0) {
|
||||
LOG_PERROR("Can't listen UNIX socket '%s'", server->unix_path);
|
||||
return -1;
|
||||
}
|
||||
if (evhttp_accept_socket(server->run->http, server->run->unix_fd) < 0) {
|
||||
LOG_PERROR("Can't evhttp_accept_socket() UNIX socket '%s'", server->unix_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
|
||||
|
||||
} else {
|
||||
LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
||||
if (evhttp_bind_socket(server->run->http, server->host, server->port) < 0) {
|
||||
LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO("Listening HTTP on [%s]:%u", server->host, server->port);
|
||||
}
|
||||
|
||||
LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <event2/http.h>
|
||||
#include <event2/util.h>
|
||||
|
||||
#include "tools.h"
|
||||
#include "stream.h"
|
||||
@@ -65,6 +68,7 @@ struct exposed_t {
|
||||
struct http_server_runtime_t {
|
||||
struct event_base *base;
|
||||
struct evhttp *http;
|
||||
evutil_socket_t unix_fd;
|
||||
struct event *refresh;
|
||||
struct stream_t *stream;
|
||||
struct exposed_t *exposed;
|
||||
@@ -76,6 +80,9 @@ struct http_server_runtime_t {
|
||||
struct http_server_t {
|
||||
char *host;
|
||||
unsigned port;
|
||||
char *unix_path;
|
||||
bool unix_rm;
|
||||
mode_t unix_mode;
|
||||
unsigned drop_same_frames;
|
||||
unsigned fake_width;
|
||||
unsigned fake_height;
|
||||
|
||||
74
src/main.c
74
src/main.c
@@ -40,7 +40,7 @@
|
||||
#include "http.h"
|
||||
|
||||
|
||||
static const char _short_opts[] = "d:i:x:y:f:a:z:tn:w:q:c:s:p:r:h";
|
||||
static const char _short_opts[] = "d:i:x:y:f:a:z:tn:w:q:c:s:p:u:ro:e:h";
|
||||
static const struct option _long_opts[] = {
|
||||
{"device", required_argument, NULL, 'd'},
|
||||
{"input", required_argument, NULL, 'i'},
|
||||
@@ -55,16 +55,16 @@ static const struct option _long_opts[] = {
|
||||
{"workers", required_argument, NULL, 'w'},
|
||||
{"quality", required_argument, NULL, 'q'},
|
||||
{"encoder", required_argument, NULL, 'c'},
|
||||
# ifdef OMX_ENCODER
|
||||
{"encoder-omx-use-ijg", required_argument, NULL, 500},
|
||||
# endif
|
||||
{"device-timeout", required_argument, NULL, 1000},
|
||||
{"device-persistent", no_argument, NULL, 1001},
|
||||
{"device-error-delay", required_argument, NULL, 1002},
|
||||
|
||||
{"host", required_argument, NULL, 's'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"drop-same-frames", required_argument, NULL, 'r'},
|
||||
{"unix", required_argument, NULL, 'u'},
|
||||
{"unix-rm", no_argument, NULL, 'r'},
|
||||
{"unix-mode", required_argument, NULL, 'o'},
|
||||
{"drop-same-frames", required_argument, NULL, 'e'},
|
||||
{"fake-width", required_argument, NULL, 2001},
|
||||
{"fake-height", required_argument, NULL, 2002},
|
||||
{"server-timeout", required_argument, NULL, 2003},
|
||||
@@ -99,51 +99,50 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
|
||||
printf("------------------\n");
|
||||
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(" -x|--width <N> -- Initial image width. Default: %d.\n\n", dev->width);
|
||||
printf(" -y|--height <N> -- Initial image height. Default: %d.\n\n", dev->height);
|
||||
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(" -m|--format <fmt> -- Image format.\n");
|
||||
printf(" Available: %s; default: YUYV.\n\n", FORMATS_STR);
|
||||
printf(" -a|--tv-standard <std> -- Force TV standard.\n");
|
||||
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(" Useful if the device produces small-sized garbage frames.\n\n");
|
||||
printf(" -t|--dv-timings -- Enable DV timings queriyng and events processing.\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(" Each buffer may processed using an intermediate thread.\n");
|
||||
printf(" Default: %d (number of CPU cores + 1)\n\n", dev->n_buffers);
|
||||
printf(" -w|--workers <N> -- The number of compressing threads. Default: %d (== --buffers).\n\n", dev->n_workers);
|
||||
printf(" -q|--quality <N> -- Set quality of JPEG encoding from 1 to 100 (best). Default: %d.\n\n", encoder->quality);
|
||||
printf(" --encoder <type> -- Use specified encoder. It may affects to workers number.\n");
|
||||
printf(" Default: %u (number of CPU cores + 1)\n\n", dev->n_buffers);
|
||||
printf(" -w|--workers <N> -- The number of compressing 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(" -c|--encoder <type> -- Use specified encoder. It may affects to workers number.\n");
|
||||
printf(" -- Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
|
||||
# ifdef OMX_ENCODER
|
||||
printf(" --encoder-omx-use-ijg -- Use the standard IJG quality tables when encoding images using OMX.\n");
|
||||
printf(" Default: disabled.\n\n");
|
||||
# endif
|
||||
printf(" --device-timeout <seconds> -- Timeout for device querying. Default: %d\n\n", dev->timeout);
|
||||
printf(" --device-timeout <seconds> -- Timeout for device querying. Default: %u\n\n", dev->timeout);
|
||||
printf(" --device-persistent -- Don't re-initialize device on timeout. Default: disabled.\n\n");
|
||||
printf(" --device-error-delay <seconds> -- Delay before trying to connect to the device again\n");
|
||||
printf(" after a timeout. Default: %d\n\n", dev->error_delay);
|
||||
printf(" after a timeout. Default: %u\n\n", dev->error_delay);
|
||||
printf("HTTP server options:\n");
|
||||
printf("--------------------\n");
|
||||
printf(" --host <address> -- Listen on Hostname or IP. Default: %s\n\n", server->host);
|
||||
printf(" --port <N> -- Bind to this TCP port. Default: %d\n\n", server->port);
|
||||
printf(" --drop-same-frames <N> -- Don't send same frames to clients, but no more than specified number.\n");
|
||||
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(" -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(" -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(" 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(" or webcams, it's useless. 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(" --server-timeout <seconds> -- Timeout for client connections. Default: %d\n\n", server->timeout);
|
||||
printf(" --server-timeout <seconds> -- Timeout for client connections. Default: %u\n\n", server->timeout);
|
||||
printf("Misc options:\n");
|
||||
printf("-------------\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(" Available levels: 0=info, 1=performance, 2=verbose, 3=debug.\n");
|
||||
printf(" Default: %d.\n\n", log_level);
|
||||
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(" Default: %u.\n\n", log_level);
|
||||
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(" --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");
|
||||
}
|
||||
@@ -153,9 +152,9 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
||||
{ _dest = _value; break; }
|
||||
|
||||
# define OPT_UNSIGNED(_dest, _name, _min, _max) \
|
||||
{ errno = 0; int _tmp = strtol(optarg, NULL, 0); \
|
||||
if (errno || _tmp < _min || _tmp > _max) \
|
||||
{ printf("Invalid value for '%s=%u'; min=%u; max=%u\n", _name, _tmp, _min, _max); return -1; } \
|
||||
{ errno = 0; char *_end = NULL; int _tmp = strtol(optarg, &_end, 0); \
|
||||
if (errno || *_end || _tmp < _min || _tmp > _max) \
|
||||
{ printf("Invalid value for '%s=%s'; min=%u; max=%u\n", _name, optarg, _min, _max); return -1; } \
|
||||
_dest = _tmp; break; }
|
||||
|
||||
# define OPT_PARSE(_dest, _func, _invalid, _name) \
|
||||
@@ -163,6 +162,12 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
||||
{ printf("Unknown " _name ": %s\n", optarg); return -1; } \
|
||||
break; }
|
||||
|
||||
# define OPT_CHMOD(_dest, _name) \
|
||||
{ errno = 0; char *_end = NULL; int _tmp = strtol(optarg, &_end, 8); \
|
||||
if (errno || *_end) \
|
||||
{ printf("Invalid value for '%s=%s'\n", _name, optarg); return -1; } \
|
||||
_dest = _tmp; break; }
|
||||
|
||||
int index;
|
||||
int ch;
|
||||
|
||||
@@ -171,8 +176,8 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
||||
switch (ch) {
|
||||
case 'd': OPT_SET(dev->path, optarg);
|
||||
case 'i': OPT_UNSIGNED(dev->input, "--input", 0, 128);
|
||||
case 'x': OPT_UNSIGNED(dev->width, "--width", 320, 1920);
|
||||
case 'y': OPT_UNSIGNED(dev->height, "--height", 180, 1200);
|
||||
case 'x': OPT_UNSIGNED(dev->width, "--width", VIDEO_MIN_WIDTH, VIDEO_MAX_WIDTH);
|
||||
case 'y': OPT_UNSIGNED(dev->height, "--height", VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT);
|
||||
# pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
# pragma GCC diagnostic push
|
||||
case 'm': OPT_PARSE(dev->format, device_parse_format, FORMAT_UNKNOWN, "pixel format");
|
||||
@@ -185,16 +190,16 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
||||
case 'w': OPT_UNSIGNED(dev->n_workers, "--workers", 1, 32);
|
||||
case 'q': OPT_UNSIGNED(encoder->quality, "--quality", 1, 100);
|
||||
case 'c': OPT_PARSE(encoder->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, "encoder type");
|
||||
# ifdef OMX_ENCODER
|
||||
case 500: OPT_SET(encoder->omx_use_ijg, true);
|
||||
# endif
|
||||
case 1000: OPT_UNSIGNED(dev->timeout, "--device-timeout", 1, 60);
|
||||
case 1001: OPT_SET(dev->persistent, true);
|
||||
case 1002: OPT_UNSIGNED(dev->error_delay, "--device-error-delay", 1, 60);
|
||||
|
||||
case 's': OPT_SET(server->host, optarg);
|
||||
case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535);
|
||||
case 'r': OPT_UNSIGNED(server->drop_same_frames, "--drop-same-frames", 0, 30);
|
||||
case 'u': OPT_SET(server->unix_path, optarg);
|
||||
case 'r': OPT_SET(server->unix_rm, true);
|
||||
case 'o': OPT_CHMOD(server->unix_mode, "--unix-mode");
|
||||
case 'e': OPT_UNSIGNED(server->drop_same_frames, "--drop-same-frames", 0, 30);
|
||||
case 2001: OPT_UNSIGNED(server->fake_width, "--fake-width", 0, 1920);
|
||||
case 2002: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200);
|
||||
case 2003: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);
|
||||
@@ -210,6 +215,7 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
||||
}
|
||||
}
|
||||
|
||||
# undef OPT_CHMOD
|
||||
# undef OPT_PARSE
|
||||
# undef OPT_UNSIGNED
|
||||
# undef OPT_SET
|
||||
|
||||
@@ -51,7 +51,7 @@ static int _i_omx = 0;
|
||||
static int _omx_init_component(struct omx_encoder_t *omx);
|
||||
static int _omx_init_disable_ports(struct omx_encoder_t *omx);
|
||||
static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev);
|
||||
static int _omx_setup_output(struct omx_encoder_t *omx, const unsigned quality, const bool use_ijg);
|
||||
static int _omx_setup_output(struct omx_encoder_t *omx, const unsigned quality);
|
||||
static int _omx_encoder_clear_ports(struct omx_encoder_t *omx);
|
||||
|
||||
static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder,
|
||||
@@ -148,7 +148,7 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
|
||||
free(omx);
|
||||
}
|
||||
|
||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, const unsigned quality, const bool use_ijg) {
|
||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, const unsigned quality) {
|
||||
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, co
|
||||
if (_omx_setup_input(omx, dev) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (_omx_setup_output(omx, quality, use_ijg) < 0) {
|
||||
if (_omx_setup_output(omx, quality) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (component_set_state(&omx->encoder, OMX_StateExecuting) < 0) {
|
||||
@@ -329,7 +329,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _omx_setup_output(struct omx_encoder_t *omx, const unsigned quality, const bool use_ijg) {
|
||||
static int _omx_setup_output(struct omx_encoder_t *omx, const unsigned quality) {
|
||||
OMX_ERRORTYPE error;
|
||||
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
||||
|
||||
@@ -365,7 +365,7 @@ static int _omx_setup_output(struct omx_encoder_t *omx, const unsigned quality,
|
||||
}
|
||||
}
|
||||
|
||||
if (use_ijg) {
|
||||
{
|
||||
OMX_PARAM_IJGSCALINGTYPE ijg;
|
||||
|
||||
OMX_INIT_STRUCTURE(ijg);
|
||||
|
||||
@@ -51,5 +51,5 @@ struct omx_encoder_t {
|
||||
struct omx_encoder_t *omx_encoder_init();
|
||||
void omx_encoder_destroy(struct omx_encoder_t *omx);
|
||||
|
||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, const unsigned quality, const bool use_ijg);
|
||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, const unsigned quality);
|
||||
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, const unsigned index);
|
||||
|
||||
18
src/stream.c
18
src/stream.c
@@ -189,7 +189,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
// For example a VGA (640x480) webcam picture is normally >= 8kByte large,
|
||||
// corrupted frames are smaller.
|
||||
if (buf_info.bytesused < stream->dev->min_frame_size) {
|
||||
LOG_DEBUG("Dropping too small frame sized %d bytes, assuming it as broken", buf_info.bytesused);
|
||||
LOG_DEBUG("Dropping too small frame sized %u bytes, assuming it as broken", buf_info.bytesused);
|
||||
goto pass_frame;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
LOG_VERBOSE("Fluency: delay=%.03Lf; grab_after=%.03Lf", fluency_delay, grab_after);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Grabbed a new frame to buffer %d", buf_info.index);
|
||||
LOG_DEBUG("Grabbed a new frame to buffer %u", buf_info.index);
|
||||
pool.workers[free_worker_number].ctx.buf_info = buf_info;
|
||||
|
||||
if (!oldest_worker) {
|
||||
@@ -343,7 +343,7 @@ static int _stream_init_loop(struct device_t *dev, struct workers_pool_t *pool)
|
||||
LOG_DEBUG("%s: *dev->stop = %d", __FUNCTION__, dev->stop);
|
||||
while (!dev->stop) {
|
||||
if ((retval = _stream_init(dev, pool)) < 0) {
|
||||
LOG_INFO("Sleeping %d seconds before new stream init ...", dev->error_delay);
|
||||
LOG_INFO("Sleeping %u seconds before new stream init ...", dev->error_delay);
|
||||
sleep(dev->error_delay);
|
||||
} else {
|
||||
break;
|
||||
@@ -378,7 +378,7 @@ static int _stream_init(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
}
|
||||
|
||||
static void _stream_init_workers(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
LOG_INFO("Spawning %d workers ...", dev->n_workers);
|
||||
LOG_INFO("Spawning %u workers ...", dev->n_workers);
|
||||
|
||||
*pool->workers_stop = false;
|
||||
A_CALLOC(pool->workers, dev->n_workers);
|
||||
@@ -434,7 +434,7 @@ static void *_stream_worker_thread(void *v_ctx) {
|
||||
# define PICTURE(_next) ctx->dev->run->pictures[ctx->buf_index]._next
|
||||
|
||||
if (!*ctx->workers_stop) {
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %d ...", ctx->number, ctx->buf_index);
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", ctx->number, ctx->buf_index);
|
||||
|
||||
PICTURE(encode_begin_time) = get_now_monotonic();
|
||||
if (encoder_compress_buffer(ctx->encoder, ctx->dev, ctx->number, ctx->buf_index) < 0) {
|
||||
@@ -453,7 +453,7 @@ static void *_stream_worker_thread(void *v_ctx) {
|
||||
A_PTHREAD_M_UNLOCK(ctx->last_comp_time_mutex);
|
||||
|
||||
LOG_VERBOSE(
|
||||
"Compressed JPEG size=%ld; time=%0.3Lf; worker=%u; buffer=%d",
|
||||
"Compressed JPEG size=%ld; time=%0.3Lf; worker=%u; buffer=%u",
|
||||
PICTURE(size), last_comp_time, ctx->number, ctx->buf_index
|
||||
);
|
||||
} else {
|
||||
@@ -470,7 +470,7 @@ static void *_stream_worker_thread(void *v_ctx) {
|
||||
A_PTHREAD_C_SIGNAL(ctx->free_workers_cond);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Bye-bye (worker %d)", ctx->number);
|
||||
LOG_DEBUG("Bye-bye (worker %u)", ctx->number);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -528,9 +528,9 @@ static int _stream_grab_buffer(struct device_t *dev, struct v4l2_buffer *buf_inf
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Got a new frame in buffer index=%d; bytesused=%d", buf_info->index, buf_info->bytesused);
|
||||
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=%d; nbuffers=%d", 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;
|
||||
|
||||
Reference in New Issue
Block a user