Compare commits

...

19 Commits
v0.45 ... v0.51

Author SHA1 Message Date
Devaev Maxim
cc25abcc00 Bump version: 0.50 → 0.51 2019-03-03 05:23:51 +03:00
Devaev Maxim
e80ee2f574 refactoring 2019-03-03 04:25:32 +03:00
Devaev Maxim
56a95c7f17 ARRAY_LEN() 2019-03-02 11:30:10 +03:00
Devaev Maxim
667e3610b2 encoder runtime 2019-03-02 11:29:57 +03:00
Devaev Maxim
142c8c84ac Bump version: 0.49 → 0.50 2019-03-01 11:36:15 +03:00
Devaev Maxim
383075d323 refactoring 2019-03-01 10:44:50 +03:00
Devaev Maxim
e2922aa820 fixed short options 2019-03-01 07:48:03 +03:00
Devaev Maxim
dc9667cf0c report about using pixelformat 2019-03-01 07:07:16 +03:00
Devaev Maxim
bbbfda0b5c Bump version: 0.48 → 0.49 2019-02-22 02:55:13 +03:00
Devaev Maxim
aa3f079ee9 fixed memory leak in http_server_destroy() 2019-02-22 02:54:03 +03:00
Devaev Maxim
59bd4e8dd2 Bump version: 0.47 → 0.48 2019-02-21 08:02:31 +03:00
Devaev Maxim
ce6184b8cd refactoring 2019-02-21 08:02:21 +03:00
Devaev Maxim
9ca511d29d Bump version: 0.46 → 0.47 2019-02-19 04:38:14 +03:00
Devaev Maxim
74de28c64a refactoring 2019-02-19 04:33:35 +03:00
Maxim Devaev
24060e8068 Update README.md 2019-01-28 17:33:57 +03:00
Maxim Devaev
3a03d48855 Update README.ru.md 2019-01-22 03:32:52 +03:00
Maxim Devaev
935a9125d6 Update README.md 2019-01-22 03:32:17 +03:00
Devaev Maxim
d4ea97ef2c Bump version: 0.45 → 0.46 2018-12-18 03:17:44 +03:00
Devaev Maxim
867aa4e52a proxy-revalidate 2018-12-18 03:17:34 +03:00
29 changed files with 755 additions and 692 deletions

View File

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

View File

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

View File

@@ -15,7 +15,7 @@
| 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 |
| Supported input formats | ![#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 |
| Option to serve files<br>with a built-in HTTP server, auth settings | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) No <sup>4</sup> | ![#00aa00](https://placehold.it/15/00aa00/000000?text=+) Yes |
@@ -60,7 +60,6 @@ $ ./ustreamer \
--format=uyvy \ # Device input format
--encoder=omx \ # Hardware encoding with OpenMAX
--dv-timings \ # Use DV-timings
--quality=20 \ # OpenMAX has a non-linear quality scale
--drop-same-frames=30 # Save that traffic
```

View File

@@ -60,7 +60,6 @@ $ ./ustreamer \
--format=uyvy \ # Настройка входного формата устройства
--encoder=omx \ # Использование аппаратного кодирования с помощью OpenMAX
--dv-timings \ # Включение DV-таймингов
--quality=20 \ # У OpenMAX нелинейная шкала качества
--drop-same-frames=30 # Экономим трафик
```

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -21,4 +22,4 @@
#pragma once
#define VERSION "0.45"
#define VERSION "0.51"

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -64,11 +65,11 @@ 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 int _device_apply_resolution(struct device_t *dev, unsigned width, 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);
static const char *_standard_to_string(const v4l2_std_id standard);
static const char *_format_to_string_auto(char *buf, size_t size, unsigned format);
static const char *_format_to_string_null(unsigned format);
static const char *_standard_to_string(v4l2_std_id standard);
struct device_t *device_init() {
@@ -97,8 +98,8 @@ void device_destroy(struct device_t *dev) {
free(dev);
}
int device_parse_format(const char *const str) {
for (unsigned index = 0; index < sizeof(_FORMATS) / sizeof(_FORMATS[0]); ++index) {
int device_parse_format(const char *str) {
for (unsigned index = 0; index < ARRAY_LEN(_FORMATS); ++index) {
if (!strcasecmp(str, _FORMATS[index].name)) {
return _FORMATS[index].format;
}
@@ -106,8 +107,8 @@ int device_parse_format(const char *const str) {
return FORMAT_UNKNOWN;
}
v4l2_std_id device_parse_standard(const char *const str) {
for (unsigned index = 1; index < sizeof(_STANDARDS) / sizeof(_STANDARDS[0]); ++index) {
v4l2_std_id device_parse_standard(const char *str) {
for (unsigned index = 1; index < ARRAY_LEN(_STANDARDS); ++index) {
if (!strcasecmp(str, _STANDARDS[index].name)) {
return _STANDARDS[index].standard;
}
@@ -285,6 +286,7 @@ static int _device_apply_dv_timings(struct device_t *dev) {
static int _device_open_format(struct device_t *dev) {
struct v4l2_format fmt;
char format_str[8];
MEMSET_ZERO(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -296,8 +298,6 @@ static int _device_open_format(struct device_t *dev) {
// Set format
LOG_DEBUG("Calling ioctl(VIDIOC_S_FMT) ...");
if (xioctl(dev->run->fd, VIDIOC_S_FMT, &fmt) < 0) {
char format_str[8];
LOG_PERROR(
"Unable to set format=%s; resolution=%ux%u",
_format_to_string_auto(format_str, 8, dev->format),
@@ -318,13 +318,12 @@ static int _device_open_format(struct device_t *dev) {
// Check format
if (fmt.fmt.pix.pixelformat != dev->format) {
char format_requested_str[8];
char format_obtained_str[8];
char *format_str_nullable;
LOG_ERROR(
"Could not obtain the requested pixelformat=%s; driver gave us %s",
_format_to_string_auto(format_requested_str, 8, dev->format),
_format_to_string_auto(format_str, 8, dev->format),
_format_to_string_auto(format_obtained_str, 8, fmt.fmt.pix.pixelformat)
);
@@ -339,7 +338,9 @@ static int _device_open_format(struct device_t *dev) {
return -1;
}
}
dev->run->format = fmt.fmt.pix.pixelformat;
LOG_INFO("Using pixelformat: %s", _format_to_string_auto(format_str, 8, dev->run->format));
return 0;
}
@@ -422,7 +423,7 @@ static void _device_open_alloc_picbufs(struct device_t *dev) {
}
}
static int _device_apply_resolution(struct device_t *dev, const unsigned width, const unsigned height) {
static int _device_apply_resolution(struct device_t *dev, unsigned width, unsigned height) {
// Тут VIDEO_MIN_* не используются из-за странностей минимального разрешения при отсутствии сигнала
// у некоторых устройств, например Auvidea B101
if (
@@ -438,12 +439,12 @@ static int _device_apply_resolution(struct device_t *dev, const unsigned width,
return 0;
}
static const char *_format_to_string_auto(char *buf, const size_t size, const unsigned format) {
static const char *_format_to_string_auto(char *buf, size_t size, unsigned format) {
assert(size >= 8);
buf[0] = format & 0x7f;
buf[1] = (format >> 8) & 0x7f;
buf[2] = (format >> 16) & 0x7f;
buf[3] = (format >> 24) & 0x7f;
buf[0] = format & 0x7F;
buf[1] = (format >> 8) & 0x7F;
buf[2] = (format >> 16) & 0x7F;
buf[3] = (format >> 24) & 0x7F;
if (format & (1 << 31)) {
buf[4] = '-';
buf[5] = 'B';
@@ -455,8 +456,8 @@ static const char *_format_to_string_auto(char *buf, const size_t size, const un
return buf;
}
static const char *_format_to_string_null(const unsigned format) {
for (unsigned index = 0; index < sizeof(_FORMATS) / sizeof(_FORMATS[0]); ++index) {
static const char *_format_to_string_null(unsigned format) {
for (unsigned index = 0; index < ARRAY_LEN(_FORMATS); ++index) {
if (format == _FORMATS[index].format) {
return _FORMATS[index].name;
}
@@ -465,7 +466,7 @@ static const char *_format_to_string_null(const unsigned format) {
}
static const char *_standard_to_string(v4l2_std_id standard) {
for (unsigned index = 0; index < sizeof(_STANDARDS) / sizeof(_STANDARDS[0]); ++index) {
for (unsigned index = 0; index < ARRAY_LEN(_STANDARDS); ++index) {
if (standard == _STANDARDS[index].standard) {
return _STANDARDS[index].name;
}

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -92,8 +93,8 @@ struct device_t {
struct device_t *device_init();
void device_destroy(struct device_t *dev);
int device_parse_format(const char *const str);
v4l2_std_id device_parse_standard(const char *const str);
int device_parse_format(const char *str);
v4l2_std_id device_parse_standard(const char *str);
int device_open(struct device_t *dev);
void device_close(struct device_t *dev);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -46,11 +47,16 @@ static const struct {
struct encoder_t *encoder_init() {
struct encoder_runtime_t *run;
struct encoder_t *encoder;
A_CALLOC(run, 1);
run->type = ENCODER_TYPE_CPU;
A_CALLOC(encoder, 1);
encoder->type = ENCODER_TYPE_CPU;
encoder->type = run->type;
encoder->quality = 80;
encoder->run = run;
return encoder;
}
@@ -60,15 +66,16 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
#pragma GCC diagnostic pop
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
encoder->run->type = encoder->type;
if (encoder->type != ENCODER_TYPE_CPU) {
if (encoder->run->type != ENCODER_TYPE_CPU) {
LOG_DEBUG("Initializing encoder ...");
}
LOG_INFO("Using JPEG quality: %u%%", encoder->quality);
# ifdef OMX_ENCODER
if (encoder->type == ENCODER_TYPE_OMX) {
if (encoder->run->type == ENCODER_TYPE_OMX) {
if (dev->n_workers > OMX_MAX_ENCODERS) {
LOG_INFO(
"OMX-based encoder can only work with %u worker threads; forced --workers=%u",
@@ -76,11 +83,11 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
);
dev->n_workers = OMX_MAX_ENCODERS;
}
encoder->n_omxs = dev->n_workers;
encoder->run->n_omxs = dev->n_workers;
A_CALLOC(encoder->omxs, encoder->n_omxs);
for (unsigned index = 0; index < encoder->n_omxs; ++index) {
if ((encoder->omxs[index] = omx_encoder_init()) == NULL) {
A_CALLOC(encoder->run->omxs, encoder->run->n_omxs);
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
if ((encoder->run->omxs[index] = omx_encoder_init()) == NULL) {
goto use_fallback;
}
}
@@ -93,26 +100,27 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
# pragma GCC diagnostic push
use_fallback:
LOG_ERROR("Can't initialize selected encoder, using CPU instead it");
encoder->type = ENCODER_TYPE_CPU;
encoder->run->type = ENCODER_TYPE_CPU;
# pragma GCC diagnostic pop
}
void encoder_destroy(struct encoder_t *encoder) {
# ifdef OMX_ENCODER
if (encoder->omxs) {
for (unsigned index = 0; index < encoder->n_omxs; ++index) {
if (encoder->omxs[index]) {
omx_encoder_destroy(encoder->omxs[index]);
if (encoder->run->omxs) {
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
if (encoder->run->omxs[index]) {
omx_encoder_destroy(encoder->run->omxs[index]);
}
}
free(encoder->omxs);
free(encoder->run->omxs);
}
# endif
free(encoder->run);
free(encoder);
}
enum encoder_type_t encoder_parse_type(const char *const str) {
for (unsigned index = 0; index < sizeof(_ENCODER_TYPES) / sizeof(_ENCODER_TYPES[0]); ++index) {
enum encoder_type_t encoder_parse_type(const char *str) {
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
return _ENCODER_TYPES[index].type;
}
@@ -123,13 +131,13 @@ enum encoder_type_t encoder_parse_type(const char *const str) {
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
#pragma GCC diagnostic pop
# 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) < 0) {
if (encoder->run->type == ENCODER_TYPE_OMX) {
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
if (omx_encoder_prepare_live(encoder->run->omxs[index], dev, encoder->quality) < 0) {
goto use_fallback;
}
}
@@ -142,24 +150,23 @@ void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
# pragma GCC diagnostic push
use_fallback:
LOG_ERROR("Can't prepare selected encoder, falling back to CPU");
encoder->type = ENCODER_TYPE_CPU;
encoder->run->type = ENCODER_TYPE_CPU;
# pragma GCC diagnostic pop
}
#pragma GCC diagnostic ignored "-Wunused-label"
#pragma GCC diagnostic push
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev,
const unsigned worker_number, const unsigned buf_index) {
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index) {
#pragma GCC diagnostic pop
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
if (encoder->type == ENCODER_TYPE_CPU) {
if (encoder->run->type == ENCODER_TYPE_CPU) {
jpeg_encoder_compress_buffer(dev, buf_index, encoder->quality);
}
# ifdef OMX_ENCODER
else if (encoder->type == ENCODER_TYPE_OMX) {
if (omx_encoder_compress_buffer(encoder->omxs[worker_number], dev, buf_index) < 0) {
else if (encoder->run->type == ENCODER_TYPE_OMX) {
if (omx_encoder_compress_buffer(encoder->run->omxs[worker_number], dev, buf_index) < 0) {
goto error;
}
}
@@ -171,7 +178,7 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev,
# pragma GCC diagnostic push
error:
LOG_INFO("HW compressing error, falling back to CPU");
encoder->type = ENCODER_TYPE_CPU;
encoder->run->type = ENCODER_TYPE_CPU;
return -1;
# pragma GCC diagnostic pop
}

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -44,9 +45,8 @@ enum encoder_type_t {
#endif
};
struct encoder_t {
enum encoder_type_t type;
unsigned quality;
struct encoder_runtime_t {
enum encoder_type_t type;
#ifdef OMX_ENCODER
unsigned n_omxs;
struct omx_encoder_t **omxs;
@@ -54,13 +54,20 @@ struct encoder_t {
};
struct encoder_t {
enum encoder_type_t type;
unsigned quality;
struct encoder_runtime_t *run;
};
struct encoder_t *encoder_init();
void encoder_destroy(struct encoder_t *encoder);
enum encoder_type_t encoder_parse_type(const char *const str);
enum encoder_type_t encoder_parse_type(const char *str);
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev);
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev);
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev,
const unsigned worker_number, const unsigned buf_index);
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -65,7 +66,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
static void _http_callback_stream_error(struct bufferevent *buf_event, short what, void *v_ctx);
static void _http_exposed_refresh(int fd, short event, void *v_server);
static void _http_queue_send_stream(struct http_server_t *server, const bool stream_updated, const bool picture_updated);
static void _http_queue_send_stream(struct http_server_t *server, bool stream_updated, bool picture_updated);
static bool _expose_new_picture(struct http_server_t *server);
static bool _expose_blank_picture(struct http_server_t *server);
@@ -117,6 +118,14 @@ void http_server_destroy(struct http_server_t *server) {
event_base_free(server->run->base);
libevent_global_shutdown();
for (struct stream_client_t *client = server->run->stream_clients; client != NULL;) {
struct stream_client_t *next = client->next;
free(client->key);
free(client);
client = next;
}
free(server->run->exposed->picture.data);
free(server->run->exposed);
free(server->run);
@@ -252,14 +261,16 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
assert((buf = evbuffer_new()));
assert(evbuffer_add_printf(buf,
"{\"ok\": true, \"result\":"
" {\"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
" \"online\": %s, \"quality\": %u, \"desired_fps\": %u, \"captured_fps\": %u},"
"{\"ok\": true, \"result\": {"
" \"encoder\": {\"fallback\": %s, \"quality\": %u},"
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
bool_to_string(server->run->stream->encoder->type != server->run->stream->encoder->run->type),
server->run->stream->encoder->quality,
(server->fake_width ? server->fake_width : server->run->exposed->width),
(server->fake_height ? server->fake_height : server->run->exposed->height),
bool_to_string(server->run->exposed->online),
server->run->stream->encoder->quality,
server->run->stream->dev->desired_fps,
server->run->exposed->captured_fps,
server->run->exposed->queued_fps,
@@ -296,7 +307,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
assert(!evbuffer_add(buf, (const void *)EXPOSED(picture.data), EXPOSED(picture.size)));
ADD_HEADER("Access-Control-Allow-Origin:", "*");
ADD_HEADER("Cache-Control", "no-store, no-cache, must-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");
ADD_HEADER("Pragma", "no-cache");
ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT");
@@ -447,7 +458,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
assert(evbuffer_add_printf(buf,
"HTTP/1.0 200 OK" RN
"Access-Control-Allow-Origin: *" RN
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0" RN
"Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0" RN
"Pragma: no-cache" RN
"Expires: Mon, 3 Jan 2000 12:34:56 GMT" RN
"Set-Cookie: stream_client=%s/%s; path=/; max-age=30" RN
@@ -562,7 +573,7 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
free(client);
}
static void _http_queue_send_stream(struct http_server_t *server, const bool stream_updated, const bool picture_updated) {
static void _http_queue_send_stream(struct http_server_t *server, bool stream_updated, bool picture_updated) {
struct evhttp_connection *conn;
struct bufferevent *buf_event;
long long now;

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# This source file based on code of MJPG-Streamer. #
@@ -48,24 +49,27 @@ struct _mjpg_destination_mgr {
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, unsigned long *written);
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
static void _jpeg_write_scanlines_yuyv(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
unsigned width, unsigned height);
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg,
static void _jpeg_write_scanlines_uyvy(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
unsigned width, unsigned height);
static void _jpeg_write_scanlines_rgb565(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
unsigned width, unsigned height);
static void _jpeg_init_destination(j_compress_ptr jpeg);
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
static void _jpeg_term_destination(j_compress_ptr jpeg);
void jpeg_encoder_compress_buffer(struct device_t *dev, const unsigned index, const unsigned quality) {
void jpeg_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned quality) {
// This function based on compress_image_to_jpeg() from mjpg-streamer
struct jpeg_compress_struct jpeg;
@@ -92,6 +96,7 @@ void jpeg_encoder_compress_buffer(struct device_t *dev, const unsigned index, co
# define WRITE_SCANLINES(_func) \
_func(&jpeg, line_buffer, dev->run->hw_buffers[index].start, dev->run->width, dev->run->height)
switch (dev->run->format) {
// https://www.fourcc.org/yuv.php
case V4L2_PIX_FMT_YUYV: WRITE_SCANLINES(_jpeg_write_scanlines_yuyv); break;
@@ -99,6 +104,7 @@ void jpeg_encoder_compress_buffer(struct device_t *dev, const unsigned index, co
case V4L2_PIX_FMT_RGB565: WRITE_SCANLINES(_jpeg_write_scanlines_rgb565); break;
default: assert(0 && "Unsupported input format for JPEG compressor");
}
# undef WRITE_SCANLINES
// TODO: process jpeg errors:
@@ -127,9 +133,12 @@ static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture,
dest->written = written;
}
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
#define NORM_COMPONENT(_x) (((_x) > 255) ? 255 : (((_x) < 0) ? 0 : (_x)))
static void _jpeg_write_scanlines_yuyv(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
unsigned width, unsigned height) {
JSAMPROW scanlines[1];
unsigned z = 0;
@@ -146,9 +155,9 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
int g = (y - (88 * u) - (183 * v)) >> 8;
int b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
*(ptr++) = NORM_COMPONENT(r);
*(ptr++) = NORM_COMPONENT(g);
*(ptr++) = NORM_COMPONENT(b);
if (z++) {
z = 0;
@@ -161,9 +170,10 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
}
}
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
static void _jpeg_write_scanlines_uyvy(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
unsigned width, unsigned height) {
JSAMPROW scanlines[1];
unsigned z = 0;
@@ -180,9 +190,9 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
int g = (y - (88 * u) - (183 * v)) >> 8;
int b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
*(ptr++) = NORM_COMPONENT(r);
*(ptr++) = NORM_COMPONENT(g);
*(ptr++) = NORM_COMPONENT(b);
if (z++) {
z = 0;
@@ -195,9 +205,12 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
}
}
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg,
#undef NORM_COMPONENT
static void _jpeg_write_scanlines_rgb565(
struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
unsigned width, unsigned height) {
JSAMPROW scanlines[1];

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -24,4 +25,4 @@
#include "../device.h"
void jpeg_encoder_compress_buffer(struct device_t *dev, const unsigned index, const unsigned quality);
void jpeg_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned quality);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -40,7 +41,7 @@
#include "http.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 char _short_opts[] = "d:i:x:y:m:a:f:z:tb: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'},

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -30,11 +31,11 @@
#include "component.h"
static int _component_wait_port_changed(OMX_HANDLETYPE *component, const OMX_U32 port, const OMX_BOOL enabled);
static int _component_wait_state_changed(OMX_HANDLETYPE *component, const OMX_STATETYPE wanted);
static int _component_wait_port_changed(OMX_HANDLETYPE *component, OMX_U32 port, OMX_BOOL enabled);
static int _component_wait_state_changed(OMX_HANDLETYPE *component, OMX_STATETYPE wanted);
int component_enable_port(OMX_HANDLETYPE *component, const OMX_U32 port) {
int component_enable_port(OMX_HANDLETYPE *component, OMX_U32 port) {
OMX_ERRORTYPE error;
LOG_DEBUG("Enabling OMX port %u ...", port);
@@ -45,7 +46,7 @@ int component_enable_port(OMX_HANDLETYPE *component, const OMX_U32 port) {
return _component_wait_port_changed(component, port, OMX_TRUE);
}
int component_disable_port(OMX_HANDLETYPE *component, const OMX_U32 port) {
int component_disable_port(OMX_HANDLETYPE *component, OMX_U32 port) {
OMX_ERRORTYPE error;
LOG_DEBUG("Disabling OMX port %u ...", port);
@@ -56,7 +57,7 @@ int component_disable_port(OMX_HANDLETYPE *component, const OMX_U32 port) {
return _component_wait_port_changed(component, port, OMX_FALSE);
}
int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, const OMX_U32 port) {
int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, OMX_U32 port) {
OMX_ERRORTYPE error;
OMX_INIT_STRUCTURE(*portdef);
@@ -81,8 +82,8 @@ int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYP
return 0;
}
int component_set_state(OMX_HANDLETYPE *component, const OMX_STATETYPE state) {
const char *const state_str = omx_state_to_string(state);
int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state) {
const char *state_str = omx_state_to_string(state);
OMX_ERRORTYPE error;
int retries = 50;
@@ -107,7 +108,7 @@ int component_set_state(OMX_HANDLETYPE *component, const OMX_STATETYPE state) {
}
static int _component_wait_port_changed(OMX_HANDLETYPE *component, const OMX_U32 port, const OMX_BOOL enabled) {
static int _component_wait_port_changed(OMX_HANDLETYPE *component, OMX_U32 port, OMX_BOOL enabled) {
OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef;
int retries = 50;
@@ -132,7 +133,7 @@ static int _component_wait_port_changed(OMX_HANDLETYPE *component, const OMX_U32
return (portdef.bEnabled == enabled ? 0 : -1);
}
static int _component_wait_state_changed(OMX_HANDLETYPE *component, const OMX_STATETYPE wanted) {
static int _component_wait_state_changed(OMX_HANDLETYPE *component, OMX_STATETYPE wanted) {
OMX_ERRORTYPE error;
OMX_STATETYPE state;
int retries = 50;

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -38,10 +39,10 @@
}
int component_enable_port(OMX_HANDLETYPE *component, const OMX_U32 port);
int component_disable_port(OMX_HANDLETYPE *component, const OMX_U32 port);
int component_enable_port(OMX_HANDLETYPE *component, OMX_U32 port);
int component_disable_port(OMX_HANDLETYPE *component, OMX_U32 port);
int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, const OMX_U32 port);
int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, OMX_U32 port);
int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef);
int component_set_state(OMX_HANDLETYPE *component, const OMX_STATETYPE state);
int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -51,17 +52,20 @@ 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);
static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality);
static int _omx_encoder_clear_ports(struct omx_encoder_t *omx);
static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_event_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1,
UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data);
static OMX_ERRORTYPE _omx_input_required_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_input_required_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer);
static OMX_ERRORTYPE _omx_output_available_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_output_available_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer);
@@ -148,7 +152,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) {
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality) {
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
return -1;
}
@@ -167,7 +171,7 @@ int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, co
return 0;
}
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, const unsigned index) {
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index) {
OMX_ERRORTYPE error;
bool loaded = false;
@@ -329,7 +333,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) {
static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef;
@@ -433,7 +437,8 @@ static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) {
return retcode;
}
static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_event_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1,
UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data) {
@@ -449,7 +454,8 @@ static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder,
return OMX_ErrorNone;
}
static OMX_ERRORTYPE _omx_input_required_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_input_required_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer) {
// Called by OMX when the encoder component requires
@@ -462,7 +468,8 @@ static OMX_ERRORTYPE _omx_input_required_handler(UNUSED OMX_HANDLETYPE encoder,
return OMX_ErrorNone;
}
static OMX_ERRORTYPE _omx_output_available_handler(UNUSED OMX_HANDLETYPE encoder,
static OMX_ERRORTYPE _omx_output_available_handler(
UNUSED OMX_HANDLETYPE encoder,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer) {
// Called by OMX when the encoder component has filled

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -51,5 +52,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);
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, const unsigned index);
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality);
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -38,7 +39,7 @@
assert(0 && _buf); \
}
const char *omx_error_to_string(const OMX_ERRORTYPE error) {
const char *omx_error_to_string(OMX_ERRORTYPE error) {
switch (error) {
CASE_TO_STRING(OMX_ErrorNone);
CASE_TO_STRING(OMX_ErrorInsufficientResources);
@@ -69,7 +70,7 @@ const char *omx_error_to_string(const OMX_ERRORTYPE error) {
}
}
const char *omx_state_to_string(const OMX_STATETYPE state) {
const char *omx_state_to_string(OMX_STATETYPE state) {
switch (state) {
CASE_TO_STRING(OMX_StateLoaded);
CASE_TO_STRING(OMX_StateIdle);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -41,5 +42,5 @@
}
const char *omx_error_to_string(const OMX_ERRORTYPE error);
const char *omx_state_to_string(const OMX_STATETYPE state);
const char *omx_error_to_string(OMX_ERRORTYPE error);
const char *omx_state_to_string(OMX_STATETYPE state);

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -48,7 +49,7 @@ static void _stream_init_workers(struct device_t *dev, struct workers_pool_t *po
static void *_stream_worker_thread(void *v_ctx);
static void _stream_destroy_workers(struct device_t *dev, struct workers_pool_t *pool);
static int _stream_control(struct device_t *dev, const bool enable);
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);
@@ -499,7 +500,7 @@ static void _stream_destroy_workers(struct device_t *dev, struct workers_pool_t
pool->workers = NULL;
}
static int _stream_control(struct device_t *dev, const bool enable) {
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;

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -47,18 +48,18 @@
#define A_PTHREAD_C_SIGNAL(...) assert(!pthread_cond_signal(__VA_ARGS__))
#define A_PTHREAD_C_WAIT_TRUE(_var, _cond, _mutex) { while(!_var) assert(!pthread_cond_wait(_cond, _mutex)); }
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_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_PTR(_ptr) memset(_ptr, 0, sizeof(*(_ptr)))
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
#define INLINE inline __attribute__((always_inline))
#define UNUSED __attribute__((unused))
INLINE char *bool_to_string(const bool flag) {
INLINE char *bool_to_string(bool flag) {
return (flag ? "true" : "false");
}

View File

@@ -1,4 +1,5 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
@@ -32,7 +33,7 @@
#define XIOCTL_RETRIES 4
INLINE int xioctl(const int fd, const int request, void *arg) {
INLINE int xioctl(int fd, int request, void *arg) {
int retries = XIOCTL_RETRIES;
int retval = -1;

View File

@@ -44,6 +44,7 @@ def main():
text = "const char *%s = \" \\\n%s\n\";\n" % (name, text)
text = textwrap.dedent("""
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #

View File

@@ -42,7 +42,7 @@ def main():
for ch in jpg_data:
if len(rows[-1]) > 20:
rows.append([])
rows[-1].append(hex(ch))
rows[-1].append("0x%.2X" % (ch))
text = ",\n\t".join(", ".join(row) for row in rows)
text = "const unsigned char %s_JPG_DATA[] = {\n\t%s\n};\n" % (prefix, text)
@@ -51,6 +51,7 @@ def main():
text = "const unsigned %s_JPG_WIDTH = %d;\n" % (prefix, width) + text
text = textwrap.dedent("""
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #