Compare commits

...

11 Commits
v0.39 ... v0.42

Author SHA1 Message Date
Devaev Maxim
0e0e3939b2 Bump version: 0.41 → 0.42 2018-12-06 13:24:11 +03:00
Devaev Maxim
9c8d87412e streaming via unix socket 2018-12-06 13:23:58 +03:00
Maxim Devaev
3666d92819 Update README.md 2018-12-06 13:23:37 +03:00
Maxim Devaev
9388d4dd22 Update README.ru.md 2018-12-06 13:23:20 +03:00
Maxim Devaev
6cf4924e05 Update README.ru.md 2018-12-06 13:23:01 +03:00
Maxim Devaev
713e3751b4 Update README.md 2018-12-06 13:22:29 +03:00
Devaev Maxim
8e61d7c55e Bump version: 0.40 → 0.41 2018-11-12 11:37:09 +03:00
Devaev Maxim
4e42c42bae always use ijg 2018-11-12 11:36:56 +03:00
Devaev Maxim
b53e3edef1 updated readme 2018-11-12 11:02:53 +03:00
Devaev Maxim
0afbf02451 Bump version: 0.39 → 0.40 2018-11-09 22:42:24 +03:00
Devaev Maxim
dd79efd6f5 removed legacy option --every-frame 2018-11-09 22:42:07 +03:00
16 changed files with 78 additions and 44 deletions

View File

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

1
.gitignore vendored
View File

@@ -5,4 +5,5 @@
/ustreamer-*.pkg.tar.xz
/vgcore.*
/ustreamer
/*.sock
*.o

View File

@@ -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

View File

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

View File

@@ -10,9 +10,10 @@
|----------|---------------|-------------------|
| Multithreaded JPEG encoding | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Behavior when the device<br>is disconnected while streaming | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Stops the broadcast<sup>1</sup> |
| Behavior when the device<br>is disconnected while streaming | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) 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 | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) Partially yes <sup>1</sup> |
| Option to skip frames when streaming<br>static images by HTTP to save traffic | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes <sup>2</sup> | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Streaming via UNIX domain socket | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No |
| Supported input devices | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) YUYV, UYVY,<br>RGB565, ~~MJPG~~ <sup>3</sup> | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) YUYV, UYVY,<br>RGB565, MJPG |
| Access to webcam controls (focus, servos)<br>and settings such as brightness via HTTP | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) 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
```

View File

@@ -13,6 +13,7 @@
| Поведение при физическом отключении устройства<br>от сервера во время работы | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Прерывает трансляцию <sup>1</sup> |
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности изменения <br>параметров разрешения трансляции на лету<br>по сигналу источника (устройства видеозахвата) | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Есть | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) Условно есть <sup>1</sup> |
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Есть <sup>2</sup> | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Нет |
| Стрим через UNIX domain socket | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Есть | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Нет |
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Есть | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Нет |
| Поддерживаемые входные форматы устройств | ![#ffaa00](https://placehold.it/15/ffaa00/000000?text=+) YUYV, UYVY,<br>RGB565, ~~MJPG~~ <sup>3</sup> | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) YUYV, UYVY,<br>RGB565, MJPG |
| Поддержка контролов веб-камер (фокус,<br> движение сервами) и всяких настроек,<br> типа яркости, через HTTP | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Нет | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Есть |

View File

@@ -21,4 +21,4 @@
#pragma once
#define VERSION "0.39"
#define VERSION "0.42"

View File

@@ -73,7 +73,6 @@ struct device_t {
unsigned n_buffers;
unsigned n_workers;
unsigned desired_fps;
unsigned every_frame;
unsigned min_frame_size;
bool persistent;
unsigned timeout;

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -23,8 +23,13 @@
#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 <event2/event.h>
#include <event2/thread.h>
#include <event2/http.h>
@@ -105,6 +110,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 +136,53 @@ 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_path && 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 (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]:%d ...", server->host, server->port);
if (evhttp_bind_socket(server->run->http, server->host, server->port) < 0) {
LOG_PERROR("Can't bind HTTP on [%s]:%d", server->host, server->port)
return -1;
}
LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port);
}
LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port);
return 0;
}

View File

@@ -23,6 +23,7 @@
#include <event2/event.h>
#include <event2/http.h>
#include <event2/util.h>
#include "tools.h"
#include "stream.h"
@@ -65,6 +66,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 +78,8 @@ struct http_server_runtime_t {
struct http_server_t {
char *host;
unsigned port;
char *unix_path;
bool unix_rm;
unsigned drop_same_frames;
unsigned fake_width;
unsigned fake_height;

View File

@@ -40,7 +40,7 @@
#include "http.h"
static const char _short_opts[] = "d:i:x:y:f:a:e: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:er:h";
static const struct option _long_opts[] = {
{"device", required_argument, NULL, 'd'},
{"input", required_argument, NULL, 'i'},
@@ -49,22 +49,20 @@ static const struct option _long_opts[] = {
{"format", required_argument, NULL, 'm'},
{"tv-standard", required_argument, NULL, 'a'},
{"desired-fps", required_argument, NULL, 'f'},
{"every-frame", required_argument, NULL, 'e'},
{"min-frame-size", required_argument, NULL, 'z'},
{"dv-timings", no_argument, NULL, 't'},
{"buffers", required_argument, NULL, 'b'},
{"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'},
{"unix", required_argument, NULL, 'u'},
{"unix-rm", no_argument, NULL, 'e'},
{"drop-same-frames", required_argument, NULL, 'r'},
{"fake-width", required_argument, NULL, 2001},
{"fake-height", required_argument, NULL, 2002},
@@ -107,7 +105,6 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
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(" -e|--every-frame <N> -- Drop all input frames except specified. Default: disabled.\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");
@@ -119,10 +116,6 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
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(" -- 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-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");
@@ -131,6 +124,8 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
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(" --unix <path> -- Bind to UNIX domain socket. Default: disabled\n\n");
printf(" --unix-rm -- Try to remove old UNIX socket file before binding. Default: disabled\n\n");
printf(" --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");
@@ -181,22 +176,20 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
# pragma GCC diagnostic pop
case 'a': OPT_PARSE(dev->standard, device_parse_standard, STANDARD_UNKNOWN, "TV standard");
case 'f': OPT_UNSIGNED(dev->desired_fps, "--desired-fps", 0, 30);
case 'e': OPT_UNSIGNED(dev->every_frame, "--every-frame", 1, 30);
case 'z': OPT_UNSIGNED(dev->min_frame_size, "--min-frame-size", 0, 8192);
case 't': OPT_SET(dev->dv_timings, true);
case 'b': OPT_UNSIGNED(dev->n_buffers, "--buffers", 1, 32);
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 'u': OPT_SET(server->unix_path, optarg);
case 'e': OPT_SET(server->unix_rm, true);
case 'r': 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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -83,7 +83,6 @@ void stream_loop(struct stream_t *stream) {
while (_stream_init_loop(stream->dev, &pool) == 0) {
struct worker_t *oldest_worker = NULL;
struct worker_t *last_worker = NULL;
unsigned frames_count = 0;
long double grab_after = 0;
unsigned fluency_passed = 0;
unsigned captured_fps_accum = 0;
@@ -184,15 +183,6 @@ void stream_loop(struct stream_t *stream) {
}
stream->dev->run->pictures[buf_info.index].grab_time = now;
if (stream->dev->every_frame) {
if (frames_count < stream->dev->every_frame - 1) {
frames_count += 1;
LOG_DEBUG("Dropping frame %d for option --every-frame=%d", frames_count, stream->dev->every_frame);
goto pass_frame;
}
frames_count = 0;
}
// Workaround for broken, corrupted frames:
// Under low light conditions corrupted frames may get captured.
// The good thing is such frames are quite small compared to the regular pictures.