Compare commits

...

12 Commits
v0.29 ... v0.35

Author SHA1 Message Date
Devaev Maxim
8fa6db0be1 Bump version: 0.34 → 0.35 2018-11-07 03:08:10 +03:00
Devaev Maxim
d57e9864a4 stream key param 2018-11-07 03:07:58 +03:00
Devaev Maxim
077f236a43 Bump version: 0.33 → 0.34 2018-11-06 06:58:14 +03:00
Devaev Maxim
d57277877e refactoring 2018-11-06 06:57:17 +03:00
Devaev Maxim
1b0db859b2 Bump version: 0.32 → 0.33 2018-11-06 01:40:43 +03:00
Devaev Maxim
d1d8c645a8 X-UStreamer-{Width,Height} 2018-11-06 01:37:12 +03:00
Devaev Maxim
a54541ff10 Bump version: 0.31 → 0.32 2018-11-05 10:13:48 +03:00
Devaev Maxim
e5a57ac2e0 parallel OMX encoding 2018-11-05 10:12:22 +03:00
Devaev Maxim
da0e00252d Bump version: 0.30 → 0.31 2018-11-05 06:05:09 +03:00
Devaev Maxim
526ae8c3e6 added soft_fps to /ping 2018-11-05 06:04:57 +03:00
Devaev Maxim
d5081b2c18 Bump version: 0.29 → 0.30 2018-11-05 04:46:20 +03:00
Devaev Maxim
758b5558f9 report about stream client params 2018-11-05 04:46:08 +03:00
14 changed files with 135 additions and 67 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 0.29
current_version = 0.35
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.29
pkgver=0.35
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pi-kvm/ustreamer"

View File

@@ -21,4 +21,4 @@
#pragma once
#define VERSION "0.29"
#define VERSION "0.35"

View File

@@ -138,8 +138,6 @@ int device_open(struct device_t *dev) {
}
_device_open_alloc_picbufs(dev);
dev->run->n_workers = dev->n_workers;
LOG_DEBUG("Device fd=%d initialized", dev->run->fd);
return 0;

View File

@@ -54,7 +54,11 @@ struct encoder_t *encoder_init() {
return encoder;
}
void encoder_prepare(struct encoder_t *encoder) {
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
#pragma GCC diagnostic pop
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
if (encoder->type != ENCODER_TYPE_CPU) {
@@ -65,8 +69,20 @@ void encoder_prepare(struct encoder_t *encoder) {
# ifdef OMX_ENCODER
if (encoder->type == ENCODER_TYPE_OMX) {
if ((encoder->omx = omx_encoder_init()) == NULL) {
goto use_fallback;
if (dev->n_workers > OMX_MAX_ENCODERS) {
LOG_INFO(
"OMX-based encoder can only work with %u worker threads; forced --workers=%u",
OMX_MAX_ENCODERS, OMX_MAX_ENCODERS
);
dev->n_workers = OMX_MAX_ENCODERS;
}
encoder->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) {
goto use_fallback;
}
}
}
# endif
@@ -83,8 +99,13 @@ void encoder_prepare(struct encoder_t *encoder) {
void encoder_destroy(struct encoder_t *encoder) {
# ifdef OMX_ENCODER
if (encoder->omx) {
omx_encoder_destroy(encoder->omx);
if (encoder->omxs) {
for (unsigned index = 0; index < encoder->n_omxs; ++index) {
if (encoder->omxs[index]) {
omx_encoder_destroy(encoder->omxs[index]);
}
}
free(encoder->omxs);
}
# endif
free(encoder);
@@ -101,18 +122,16 @@ enum encoder_type_t encoder_parse_type(const char *const str) {
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
void encoder_prepare_for_device(struct encoder_t *encoder, struct device_t *dev) {
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
#pragma GCC diagnostic pop
# ifdef OMX_ENCODER
if (encoder->type == ENCODER_TYPE_OMX) {
if (omx_encoder_prepare_for_device(encoder->omx, dev, encoder->quality, encoder->omx_use_ijg) < 0) {
goto use_fallback;
}
if (dev->run->n_workers > 1) {
LOG_INFO("OMX encoder can only work with one worker thread; forcing n_workers to 1");
dev->run->n_workers = 1;
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) {
goto use_fallback;
}
}
}
# endif
@@ -124,27 +143,31 @@ void encoder_prepare_for_device(struct encoder_t *encoder, struct device_t *dev)
use_fallback:
LOG_ERROR("Can't prepare selected encoder, falling back to CPU");
encoder->type = ENCODER_TYPE_CPU;
dev->run->n_workers = dev->n_workers;
# pragma GCC diagnostic pop
}
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, const unsigned index) {
#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) {
#pragma GCC diagnostic pop
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
dev->run->pictures[index].encode_begin_time = get_now_monotonic();
dev->run->pictures[buf_index].encode_begin_time = get_now_monotonic();
if (encoder->type == ENCODER_TYPE_CPU) {
jpeg_encoder_compress_buffer(dev, index, encoder->quality);
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->omx, dev, index) < 0) {
if (omx_encoder_compress_buffer(encoder->omxs[worker_number], dev, buf_index) < 0) {
goto error;
}
}
# endif
dev->run->pictures[index].encode_end_time = get_now_monotonic();
dev->run->pictures[buf_index].encode_end_time = get_now_monotonic();
return 0;
@@ -153,7 +176,6 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, con
error:
LOG_INFO("HW compressing error, falling back to CPU");
encoder->type = ENCODER_TYPE_CPU;
dev->run->n_workers = dev->n_workers;
return -1;
# pragma GCC diagnostic pop
}

View File

@@ -49,7 +49,8 @@ struct encoder_t {
unsigned quality;
#ifdef OMX_ENCODER
bool omx_use_ijg;
struct omx_encoder_t *omx;
unsigned n_omxs;
struct omx_encoder_t **omxs;
#endif
};
@@ -59,6 +60,8 @@ void encoder_destroy(struct encoder_t *encoder);
enum encoder_type_t encoder_parse_type(const char *const str);
void encoder_prepare(struct encoder_t *encoder);
void encoder_prepare_for_device(struct encoder_t *encoder, struct device_t *dev);
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, const unsigned index);
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);

View File

@@ -48,6 +48,7 @@
static bool _http_get_param_true(struct evkeyvalq *params, const char *key);
static char *_http_get_param_uri(struct evkeyvalq *params, const char *key);
static void _http_callback_root(struct evhttp_request *request, void *arg);
static void _http_callback_ping(struct evhttp_request *request, void *v_server);
@@ -157,6 +158,15 @@ static bool _http_get_param_true(struct evkeyvalq *params, const char *key) {
return false;
}
static char *_http_get_param_uri(struct evkeyvalq *params, const char *key) {
const char *value_str;
if ((value_str = evhttp_find_header(params, key)) != NULL) {
return evhttp_encode_uri(value_str);
}
return NULL;
}
#define ADD_HEADER(_key, _value) \
assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value))
@@ -188,12 +198,13 @@ static void _http_callback_ping(struct evhttp_request *request, void *v_server)
assert((buf = evbuffer_new()));
assert(evbuffer_add_printf(buf,
"{\"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
" \"online\": %s, \"quality\": %u, \"captured_fps\": %u},"
" \"online\": %s, \"quality\": %u, \"soft_fps\": %u, \"captured_fps\": %u},"
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
(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->soft_fps,
server->run->exposed->captured_fps,
server->run->exposed->queued_fps,
server->run->stream_clients_count
@@ -218,7 +229,7 @@ static void _http_callback_ping(struct evhttp_request *request, void *v_server)
static void _http_callback_snapshot(struct evhttp_request *request, void *v_server) {
struct http_server_t *server = (struct http_server_t *)v_server;
struct evbuffer *buf;
char time_buf[64];
char header_buf[64];
PROCESS_HEAD_REQUEST;
@@ -233,11 +244,16 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT");
# define ADD_TIME_HEADER(_key, _value) \
{ sprintf(time_buf, "%.06Lf", _value); ADD_HEADER(_key, time_buf); }
{ sprintf(header_buf, "%.06Lf", _value); ADD_HEADER(_key, header_buf); }
# define ADD_UNSIGNED_HEADER(_key, _value) \
{ sprintf(header_buf, "%u", _value); ADD_HEADER(_key, header_buf); }
ADD_TIME_HEADER("X-Timestamp", get_now_real());
ADD_HEADER("X-UStreamer-Online", bool_to_string(EXPOSED(online)));
ADD_UNSIGNED_HEADER("X-UStreamer-Width", EXPOSED(width));
ADD_UNSIGNED_HEADER("X-UStreamer-Height", EXPOSED(height));
ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture.grab_time));
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Time", EXPOSED(picture.encode_begin_time));
ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture.encode_end_time));
@@ -246,6 +262,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
ADD_TIME_HEADER("X-UStreamer-Expose-End-Time", EXPOSED(expose_end_time));
ADD_TIME_HEADER("X-UStreamer-Send-Time", get_now_monotonic());
# undef ADD_UNSUGNED_HEADER
# undef ADD_TIME_HEADER
ADD_HEADER("Content-Type", "image/jpeg");
@@ -285,6 +302,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
client->need_first_frame = true;
evhttp_parse_query(evhttp_request_get_uri(request), &params);
client->key = _http_get_param_uri(&params, "key");
client->extra_headers = _http_get_param_true(&params, "extra_headers");
client->advance_headers = _http_get_param_true(&params, "advance_headers");
client->dual_final_frames = _http_get_param_true(&params, "dual_final_frames");
@@ -306,8 +324,14 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
evhttp_connection_get_peer(conn, &client_addr, &client_port);
LOG_INFO(
"HTTP: Registered the new stream client: [%s]:%u; id=%s; clients now: %u",
client_addr, client_port, client->id, server->run->stream_clients_count
"HTTP: Registered the new stream client: [%s]:%u; id=%s;"
" advance_headers=%s; dual_final_frames=%s; clients now: %u",
client_addr,
client_port,
client->id,
bool_to_string(client->advance_headers),
bool_to_string(client->dual_final_frames),
server->run->stream_clients_count
);
buf_event = evhttp_connection_get_bufferevent(conn);
@@ -368,10 +392,11 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"Cache-Control: no-store, no-cache, must-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_id=%s; path=/; max-age=30" RN
"Set-Cookie: stream_client_key=%s; stream_client_id=%s; path=/; max-age=30" RN
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY RN
RN
"--" BOUNDARY RN,
(client->key != NULL ? client->key : "0"),
client->id
));
@@ -397,6 +422,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
if (client->extra_headers) {
assert(evbuffer_add_printf(buf,
"X-UStreamer-Online: %s" RN
"X-UStreamer-Width: %u" RN
"X-UStreamer-Height: %u" RN
"X-UStreamer-Client-FPS: %u" RN
"X-UStreamer-Grab-Time: %.06Lf" RN
"X-UStreamer-Encode-Begin-Time: %.06Lf" RN
@@ -407,6 +434,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"X-UStreamer-Send-Time: %.06Lf" RN
RN,
bool_to_string(EXPOSED(online)),
EXPOSED(width),
EXPOSED(height),
client->fps,
EXPOSED(picture.grab_time),
EXPOSED(picture.encode_begin_time),
@@ -435,10 +464,10 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client);
bufferevent_enable(buf_event, EV_READ);
# undef BOUNDARY
# undef RN
# undef ADD_ADVANCE_HEADERS
# undef EXPOSED
# undef ADD_ADVANCE_HEADERS
# undef RN
# undef BOUNDARY
}
static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UNUSED short what, void *v_client) {
@@ -469,6 +498,7 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
if (client->next != NULL) {
client->next->prev = client->prev;
}
free(client->key);
free(client);
}
@@ -614,8 +644,8 @@ static bool _expose_new_picture(struct http_server_t *server) {
EXPOSED(expose_end_time) - EXPOSED(expose_begin_time)
);
# undef STREAM
# undef EXPOSED
# undef STREAM
return true; // Updated
}

View File

@@ -32,6 +32,7 @@ struct stream_client_t {
struct http_server_t *server;
struct evhttp_request *request;
char *key;
bool extra_headers;
bool advance_headers;
bool dual_final_frames;

View File

@@ -213,9 +213,9 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
}
}
# undef OPT_SET
# undef OPT_UNSIGNED
# undef OPT_PARSE
# undef OPT_UNSIGNED
# undef OPT_SET
return 0;
}
@@ -287,7 +287,7 @@ int main(int argc, char *argv[]) {
if ((exit_code = _parse_options(argc, argv, dev, encoder, server)) == 0) {
_install_signal_handlers();
encoder_prepare(encoder);
encoder_prepare(encoder, dev);
pthread_t stream_loop_tid;
pthread_t server_loop_tid;

View File

@@ -45,6 +45,9 @@
#define OUTPUT_PORT 341
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);
@@ -77,17 +80,20 @@ struct omx_encoder_t *omx_encoder_init() {
A_CALLOC(omx, 1);
LOG_INFO("Initializing OMX JPEG encoder ...");
assert(_i_omx >= 0);
if (_i_omx == 0) {
LOG_INFO("Initializing BCM ...");
bcm_host_init();
LOG_DEBUG("Initializing BCM ...");
bcm_host_init();
LOG_DEBUG("Initializing OMX ...");
if ((error = OMX_Init()) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't initialize OMX");
goto error;
LOG_INFO("Initializing OMX ...");
if ((error = OMX_Init()) != OMX_ErrorNone) {
LOG_OMX_ERROR(error, "Can't initialize OMX");
goto error;
}
}
omx->i_omx = true;
_i_omx += 1;
LOG_INFO("Initializing OMX JPEG encoder ...");
if (vcos_semaphore_create(&omx->handler_lock, "handler_lock", 0) != VCOS_SUCCESS) {
LOG_ERROR("Can't create VCOS semaphore");
@@ -129,14 +135,20 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
}
}
if (omx->i_omx) {
assert(_i_omx >= 0);
_i_omx -= 1;
if (_i_omx == 0) {
LOG_INFO("Destroying OMX ...");
OMX_Deinit();
LOG_INFO("Destroying BCM ...");
bcm_host_deinit();
}
bcm_host_deinit();
free(omx);
}
int omx_encoder_prepare_for_device(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, const bool use_ijg) {
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
return -1;
}

View File

@@ -29,6 +29,9 @@
#include "../device.h"
#define OMX_MAX_ENCODERS 3
struct omx_encoder_t {
OMX_HANDLETYPE encoder;
OMX_BUFFERHEADERTYPE *input_buffer;
@@ -38,7 +41,6 @@ struct omx_encoder_t {
bool failed;
VCOS_SEMAPHORE_T handler_lock;
bool i_omx;
bool i_handler_lock;
bool i_encoder;
bool i_input_port_enabled;
@@ -49,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_for_device(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, const bool use_ijg);
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, const unsigned index);

View File

@@ -78,5 +78,5 @@ const char *omx_state_to_string(const OMX_STATETYPE state) {
}
}
#undef CASE_TO_STRING
#undef CASE_ASSERT
#undef CASE_TO_STRING

View File

@@ -116,7 +116,7 @@ void stream_loop(struct stream_t *stream) {
LOG_PERF("##### Raw frame accepted; worker = %u", free_worker_number);
} else {
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
if (!pool.workers[number].has_job && (free_worker_number == -1
|| pool.workers[free_worker_number].job_start_time < pool.workers[number].job_start_time
)) {
@@ -325,16 +325,16 @@ static long double _stream_get_fluency_delay(struct device_t *dev, struct worker
long double min_delay;
long double soft_delay;
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
for (unsigned number = 0; number < dev->n_workers; ++number) {
A_PTHREAD_M_LOCK(&pool->workers[number].last_comp_time_mutex);
if (pool->workers[number].last_comp_time > 0) {
comp_time += pool->workers[number].last_comp_time;
}
A_PTHREAD_M_UNLOCK(&pool->workers[number].last_comp_time_mutex);
}
comp_time = comp_time / dev->run->n_workers; // Среднее время работы воркеров
comp_time = comp_time / dev->n_workers; // Среднее время работы воркеров
min_delay = comp_time / dev->run->n_workers; // Минимальное время работы размазывается на N воркеров
min_delay = comp_time / dev->n_workers; // Минимальное время работы размазывается на N воркеров
soft_delay = ((long double)1) / dev->soft_fps; // Искусственное время задержки на основе желаемого FPS
if (min_delay > 0) {
@@ -372,7 +372,7 @@ static int _stream_init(struct device_t *dev, struct workers_pool_t *pool) {
goto error;
}
encoder_prepare_for_device(pool->encoder, dev);
encoder_prepare_live(pool->encoder, dev);
_stream_init_workers(dev, pool);
@@ -384,15 +384,15 @@ 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->run->n_workers);
LOG_INFO("Spawning %d workers ...", dev->n_workers);
*pool->workers_stop = false;
A_CALLOC(pool->workers, dev->run->n_workers);
A_CALLOC(pool->workers, dev->n_workers);
A_PTHREAD_M_INIT(&pool->free_workers_mutex);
A_PTHREAD_C_INIT(&pool->free_workers_cond);
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
for (unsigned number = 0; number < dev->n_workers; ++number) {
pool->free_workers += 1;
A_PTHREAD_M_INIT(&pool->workers[number].has_job_mutex);
@@ -440,7 +440,7 @@ static void *_stream_worker_thread(void *v_ctx) {
if (!*ctx->workers_stop) {
LOG_DEBUG("Worker %u compressing JPEG from buffer %d ...", ctx->number, ctx->buf_index);
if (encoder_compress_buffer(ctx->encoder, ctx->dev, ctx->buf_index) < 0) {
if (encoder_compress_buffer(ctx->encoder, ctx->dev, ctx->number, ctx->buf_index) < 0) {
*ctx->job_failed = true;
}
@@ -479,7 +479,7 @@ static void _stream_destroy_workers(struct device_t *dev, struct workers_pool_t
LOG_INFO("Destroying workers ...");
*pool->workers_stop = true;
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
for (unsigned number = 0; number < dev->n_workers; ++number) {
A_PTHREAD_M_LOCK(&pool->workers[number].has_job_mutex);
pool->workers[number].has_job = true; // Final job: die
A_PTHREAD_M_UNLOCK(&pool->workers[number].has_job_mutex);

View File

@@ -72,7 +72,7 @@ struct worker_t {
};
struct workers_pool_t {
struct worker_t*workers;
struct worker_t *workers;
bool *workers_stop;
pthread_mutex_t free_workers_mutex;