mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-01 21:26:33 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d054fa9a8 | ||
|
|
f2863d1108 | ||
|
|
281e1b36ce | ||
|
|
160ad5c10f | ||
|
|
667137638b | ||
|
|
b5dffa927a | ||
|
|
651a82d1ca | ||
|
|
d963c95af8 | ||
|
|
7c524a1196 | ||
|
|
0c85aad5a2 | ||
|
|
5515654497 | ||
|
|
5955310bf3 | ||
|
|
a9df6da912 | ||
|
|
f3c56d5774 | ||
|
|
9a86793923 | ||
|
|
93c6248fdb | ||
|
|
ec738b18dc | ||
|
|
6029408564 | ||
|
|
8734834341 | ||
|
|
809f86955d | ||
|
|
2f557617d8 | ||
|
|
35c8196103 | ||
|
|
c71df1bb25 | ||
|
|
bc107d2870 | ||
|
|
294ed36b8f |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 0.16
|
current_version = 0.23
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -6,7 +6,7 @@ LDFLAGS ?=
|
|||||||
|
|
||||||
# =====
|
# =====
|
||||||
CC = gcc
|
CC = gcc
|
||||||
LIBS = -lm -ljpeg -pthread -levent -levent_pthreads
|
LIBS = -lm -ljpeg -pthread -levent -levent_pthreads -luuid
|
||||||
override CFLAGS += -c -std=c99 -Wall -Wextra -D_GNU_SOURCE
|
override CFLAGS += -c -std=c99 -Wall -Wextra -D_GNU_SOURCE
|
||||||
SOURCES = $(shell ls src/*.c src/jpeg/*.c)
|
SOURCES = $(shell ls src/*.c src/jpeg/*.c)
|
||||||
OBJECTS = $(SOURCES:.c=.o)
|
OBJECTS = $(SOURCES:.c=.o)
|
||||||
|
|||||||
4
PKGBUILD
4
PKGBUILD
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=0.16
|
pkgver=0.23
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
||||||
url="https://github.com/pi-kvm/ustreamer"
|
url="https://github.com/pi-kvm/ustreamer"
|
||||||
license=(GPL)
|
license=(GPL)
|
||||||
arch=(i686 x86_64 armv6h armv7h)
|
arch=(i686 x86_64 armv6h armv7h)
|
||||||
depends=(libjpeg libevent)
|
depends=(libjpeg libevent libutil-linux)
|
||||||
# optional: raspberrypi-firmware for OMX JPEG compressor
|
# optional: raspberrypi-firmware for OMX JPEG compressor
|
||||||
makedepends=(gcc make)
|
makedepends=(gcc make)
|
||||||
source=("$url/archive/v$pkgver.tar.gz")
|
source=("$url/archive/v$pkgver.tar.gz")
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -17,9 +17,9 @@
|
|||||||
| Возможность сервить файлы встроенным<br>HTTP-сервером, настройки авторизации |  Нет <sup>4</sup> |  Есть |
|
| Возможность сервить файлы встроенным<br>HTTP-сервером, настройки авторизации |  Нет <sup>4</sup> |  Есть |
|
||||||
|
|
||||||
Сносочки:
|
Сносочки:
|
||||||
* ```1``` Для mjpg-streamer существует [мой патч](https://github.com/jacksonliam/mjpg-streamer/pull/164), предотвращающий зависание при отключении устройства и добавляющий поддержку DV-таймингов, однако трансляция при этом все равно прерывается. В данный момент этот патч не принят в апстрим, и я даже не гарантирую его стопроцентную работоспособность. Код mjpg-streamer очень плохо структурирован и чрезвычайно запутан, и я мог что-то упустить. Собственно, это одна из причин, почему µStreamer был написан с нуля.
|
* ```1``` Для mjpg-streamer существует [мой патч](https://github.com/jacksonliam/mjpg-streamer/pull/164), предотвращающий зависание при отключении устройства и добавляющий поддержку DV-таймингов, однако трансляция при этом все равно прерывается. В данный момент этот патч не принят в апстрим, и я даже не гарантирую его стопроцентную работоспособность. Код mjpg-streamer очень плохо структурирован, чрезвычайно запутан, и я мог что-то упустить. Собственно, это одна из причин, почему µStreamer был написан с нуля.
|
||||||
|
|
||||||
* ```2``` Это фича позволяет в несколько раз снизить объем исходящего трафика при трансляции HDMI, однако увеличивает загрузку процессора и добавляет небольшую задержку. Суть в том, что HDMI - полностью цифровой интерфейс, и новый захваченный фрейм может быть идентичен предыдущему в точности до байта. В этом случае нет нужды передавать одну и ту же картинку по сети несколько раз в секунду. При использовании опции `--drop-same-frames=20`, µStreamer будет дропать все одинаковые фреймы, но не более 20. Новый фрейм сравнивается с предыдущим сначала по длине, а затем помощью ```memcmp()```.
|
* ```2``` Это фича позволяет в несколько раз снизить объем исходящего трафика при трансляции HDMI, однако немного увеличивает загрузку процессора. Суть в том, что HDMI - полностью цифровой интерфейс, и новый захваченный фрейм может быть идентичен предыдущему в точности до байта. В этом случае нет нужды передавать одну и ту же картинку по сети несколько раз в секунду. При использовании опции `--drop-same-frames=20`, µStreamer будет дропать все одинаковые фреймы, но не более 20 подряд. Новый фрейм сравнивается с предыдущим сначала по длине, а затем помощью ```memcmp()```.
|
||||||
|
|
||||||
* ```3``` Поскольку µStreamer писался в первую очередь для устройств видеозахвата, в нем реализованы только те форматы, которые для них были нужны. MJPG в контексте входных данных означает, что устройство умеет самостоятельно сжимать картинку в JPEG и отдавать ее программе, что позволяет значительно снизить загрузку процессора и избавить его от необходимости кодировать картинку софтом. Этот формат поддерживается большинством веб-камер, но не поддерживается ни одним из встреченных мной устройств видеозахвата; его не умеет ни [Auvidea B101](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/), ни [EasyCap UTV 007](https://www.amazon.com/dp/B0126O0RDC). Нет никаких технических сложностей добавить поддержку аппаратного MJPG источника, но у меня просто пока не дошли до этого руки.
|
* ```3``` Поскольку µStreamer писался в первую очередь для устройств видеозахвата, в нем реализованы только те форматы, которые для них были нужны. MJPG в контексте входных данных означает, что устройство умеет самостоятельно сжимать картинку в JPEG и отдавать ее программе, что позволяет значительно снизить загрузку процессора и избавить его от необходимости кодировать картинку софтом. Этот формат поддерживается большинством веб-камер, но не поддерживается ни одним из встреченных мной устройств видеозахвата; его не умеет ни [Auvidea B101](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/), ни [EasyCap UTV 007](https://www.amazon.com/dp/B0126O0RDC). Нет никаких технических сложностей добавить поддержку аппаратного MJPG источника, но у меня просто пока не дошли до этого руки.
|
||||||
|
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
-----
|
-----
|
||||||
# Сборка
|
# Сборка
|
||||||
Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads``` и ```libjpeg8```/```libjpeg-turbo```.
|
Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo``` и ```libuuid```.
|
||||||
|
|
||||||
На Raspberry Pi програма автоматически собирается с поддержкой OpenMAX, если обнаружит нужные хедеры в ```/opt/vc/include```.
|
На Raspberry Pi програма автоматически собирается с поддержкой OpenMAX IL, если обнаружит нужные хедеры в ```/opt/vc/include```.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone --depth=1 https://github.com/pi-kvm/ustreamer
|
$ git clone --depth=1 https://github.com/pi-kvm/ustreamer
|
||||||
@@ -57,6 +57,8 @@ $ ./ustreamer \
|
|||||||
--format=uyvy \ # Настройка входного формата устройства
|
--format=uyvy \ # Настройка входного формата устройства
|
||||||
--encoder=omx \ # Использование аппаратного кодирования с помощью OpenMAX
|
--encoder=omx \ # Использование аппаратного кодирования с помощью OpenMAX
|
||||||
--dv-timings # Включение DV-таймингов
|
--dv-timings # Включение DV-таймингов
|
||||||
|
--quality=20 # У OpenMAX нелинейная шкала качества
|
||||||
|
--drop-same-frames=30 # Экономим трафик
|
||||||
```
|
```
|
||||||
|
|
||||||
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
|
||||||
|
|||||||
@@ -21,4 +21,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION "0.16"
|
#define VERSION "0.23"
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ 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) {
|
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, const unsigned index) {
|
||||||
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
|
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
|
||||||
|
|
||||||
dev->run->pictures[index].encode_begin_time = now_monotonic_ms();
|
dev->run->pictures[index].encode_begin_time = get_now_monotonic();
|
||||||
|
|
||||||
if (encoder->type == ENCODER_TYPE_CPU) {
|
if (encoder->type == ENCODER_TYPE_CPU) {
|
||||||
jpeg_encoder_compress_buffer(dev, index, encoder->quality);
|
jpeg_encoder_compress_buffer(dev, index, encoder->quality);
|
||||||
@@ -144,7 +144,7 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, con
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
dev->run->pictures[index].encode_end_time = now_monotonic_ms();
|
dev->run->pictures[index].encode_end_time = get_now_monotonic();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
134
src/http.c
134
src/http.c
@@ -30,6 +30,8 @@
|
|||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
#include <event2/bufferevent.h>
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
#ifndef EVTHREAD_USE_PTHREADS_IMPLEMENTED
|
#ifndef EVTHREAD_USE_PTHREADS_IMPLEMENTED
|
||||||
# error Required libevent-pthreads support
|
# error Required libevent-pthreads support
|
||||||
#endif
|
#endif
|
||||||
@@ -172,13 +174,25 @@ static void _http_callback_ping(struct evhttp_request *request, void *v_server)
|
|||||||
|
|
||||||
assert((buf = evbuffer_new()));
|
assert((buf = evbuffer_new()));
|
||||||
assert(evbuffer_add_printf(buf,
|
assert(evbuffer_add_printf(buf,
|
||||||
"{\"stream\": {\"resolution\":"
|
"{\"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
|
||||||
" {\"width\": %u, \"height\": %u},"
|
" \"online\": %s, \"quality\": %u, \"captured_fps\": %u},"
|
||||||
" \"fps\": %u, \"online\": %s}}",
|
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
|
||||||
(server->fake_width ? server->fake_width : server->run->exposed->width),
|
(server->fake_width ? server->fake_width : server->run->exposed->width),
|
||||||
(server->fake_height ? server->fake_height : server->run->exposed->height),
|
(server->fake_height ? server->fake_height : server->run->exposed->height),
|
||||||
server->run->exposed->fps, (server->run->exposed->online ? "true" : "false")
|
(server->run->exposed->online ? "true" : "false"),
|
||||||
|
server->run->stream->encoder->quality,
|
||||||
|
server->run->exposed->captured_fps,
|
||||||
|
server->run->exposed->queued_fps,
|
||||||
|
server->run->stream_clients_count
|
||||||
));
|
));
|
||||||
|
for (struct stream_client_t * client = server->run->stream_clients; client != NULL; client = client->next) {
|
||||||
|
assert(evbuffer_add_printf(buf,
|
||||||
|
"\"%s\": {\"fps\": %u}%s",
|
||||||
|
client->id, client->fps, (client->next ? ", " : "")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
assert(evbuffer_add_printf(buf, "}}}"));
|
||||||
|
|
||||||
ADD_HEADER("Content-Type", "application/json");
|
ADD_HEADER("Content-Type", "application/json");
|
||||||
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
||||||
evbuffer_free(buf);
|
evbuffer_free(buf);
|
||||||
@@ -204,16 +218,16 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
|
|||||||
# define ADD_TIME_HEADER(_key, _value) \
|
# define ADD_TIME_HEADER(_key, _value) \
|
||||||
{ sprintf(time_buf, "%.06Lf", _value); ADD_HEADER(_key, time_buf); }
|
{ sprintf(time_buf, "%.06Lf", _value); ADD_HEADER(_key, time_buf); }
|
||||||
|
|
||||||
ADD_TIME_HEADER("X-Timestamp", now_real_ms());
|
ADD_TIME_HEADER("X-Timestamp", get_now_real());
|
||||||
|
|
||||||
if (server->add_x_timings) {
|
ADD_HEADER("X-UStreamer-Online", (EXPOSED(online) ? "true" : "false"));
|
||||||
ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture.grab_time));
|
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-Begin-Time", EXPOSED(picture.encode_begin_time));
|
||||||
ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture.encode_end_time));
|
ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture.encode_end_time));
|
||||||
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Time", EXPOSED(expose_begin_time));
|
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Time", EXPOSED(expose_begin_time));
|
||||||
ADD_TIME_HEADER("X-UStreamer-Expose-End-Time", EXPOSED(expose_end_time));
|
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Time", EXPOSED(expose_cmp_time));
|
||||||
ADD_TIME_HEADER("X-UStreamer-Send-Time", now_monotonic_ms());
|
ADD_TIME_HEADER("X-UStreamer-Expose-End-Time", EXPOSED(expose_end_time));
|
||||||
}
|
ADD_TIME_HEADER("X-UStreamer-Send-Time", get_now_monotonic());
|
||||||
|
|
||||||
# undef ADD_TIME_HEADER
|
# undef ADD_TIME_HEADER
|
||||||
|
|
||||||
@@ -240,6 +254,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
struct stream_client_t *client;
|
struct stream_client_t *client;
|
||||||
char *client_addr;
|
char *client_addr;
|
||||||
unsigned short client_port;
|
unsigned short client_port;
|
||||||
|
uuid_t uuid;
|
||||||
|
|
||||||
PROCESS_HEAD_REQUEST;
|
PROCESS_HEAD_REQUEST;
|
||||||
|
|
||||||
@@ -251,6 +266,9 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
client->need_initial = true;
|
client->need_initial = true;
|
||||||
client->need_first_frame = true;
|
client->need_first_frame = true;
|
||||||
|
|
||||||
|
uuid_generate(uuid);
|
||||||
|
uuid_unparse_lower(uuid, client->id);
|
||||||
|
|
||||||
if (server->run->stream_clients == NULL) {
|
if (server->run->stream_clients == NULL) {
|
||||||
server->run->stream_clients = client;
|
server->run->stream_clients = client;
|
||||||
} else {
|
} else {
|
||||||
@@ -264,8 +282,8 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
|
|
||||||
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"HTTP: Registered the new stream client: [%s]:%u; clients now: %u",
|
"HTTP: Registered the new stream client: [%s]:%u; id=%s; clients now: %u",
|
||||||
client_addr, client_port, server->run->stream_clients_count
|
client_addr, client_port, client->id, server->run->stream_clients_count
|
||||||
);
|
);
|
||||||
|
|
||||||
buf_event = evhttp_connection_get_bufferevent(conn);
|
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||||
@@ -284,6 +302,15 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
|||||||
|
|
||||||
struct stream_client_t *client = (struct stream_client_t *)v_client;
|
struct stream_client_t *client = (struct stream_client_t *)v_client;
|
||||||
struct evbuffer *buf;
|
struct evbuffer *buf;
|
||||||
|
long double now = get_now_monotonic();
|
||||||
|
long long now_second = floor_ms(now);
|
||||||
|
|
||||||
|
if (now_second != client->fps_accum_second) {
|
||||||
|
client->fps = client->fps_accum;
|
||||||
|
client->fps_accum = 0;
|
||||||
|
client->fps_accum_second = now_second;
|
||||||
|
}
|
||||||
|
client->fps_accum += 1;
|
||||||
|
|
||||||
assert((buf = evbuffer_new()));
|
assert((buf = evbuffer_new()));
|
||||||
|
|
||||||
@@ -294,9 +321,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
|
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0" RN
|
||||||
"Pragma: no-cache" RN
|
"Pragma: no-cache" RN
|
||||||
"Expires: Mon, 3 Jan 2000 12:34:56 GMT" RN
|
"Expires: Mon, 3 Jan 2000 12:34:56 GMT" RN
|
||||||
|
"Set-Cookie: stream_client_id=%s; path=/; max-age=30" RN
|
||||||
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY RN
|
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY RN
|
||||||
RN
|
RN
|
||||||
"--" BOUNDARY RN
|
"--" BOUNDARY RN,
|
||||||
|
client->id
|
||||||
));
|
));
|
||||||
assert(!bufferevent_write_buffer(buf_event, buf));
|
assert(!bufferevent_write_buffer(buf_event, buf));
|
||||||
client->need_initial = false;
|
client->need_initial = false;
|
||||||
@@ -310,23 +339,29 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
|||||||
"X-Timestamp: %.06Lf" RN
|
"X-Timestamp: %.06Lf" RN
|
||||||
"%s",
|
"%s",
|
||||||
EXPOSED(picture.size) * sizeof(*EXPOSED(picture.data)),
|
EXPOSED(picture.size) * sizeof(*EXPOSED(picture.data)),
|
||||||
now_real_ms(), (client->server->add_x_timings ? "" : RN)
|
get_now_real(), (client->server->extra_stream_headers ? "" : RN)
|
||||||
));
|
));
|
||||||
if (client->server->add_x_timings) {
|
if (client->server->extra_stream_headers) {
|
||||||
assert(evbuffer_add_printf(buf,
|
assert(evbuffer_add_printf(buf,
|
||||||
|
"X-UStreamer-Online: %s" RN
|
||||||
|
"X-UStreamer-Client-FPS: %u" RN
|
||||||
"X-UStreamer-Grab-Time: %.06Lf" RN
|
"X-UStreamer-Grab-Time: %.06Lf" RN
|
||||||
"X-UStreamer-Encode-Begin-Time: %.06Lf" RN
|
"X-UStreamer-Encode-Begin-Time: %.06Lf" RN
|
||||||
"X-UStreamer-Encode-End-Time: %.06Lf" RN
|
"X-UStreamer-Encode-End-Time: %.06Lf" RN
|
||||||
"X-UStreamer-Expose-Begin-Time: %.06Lf" RN
|
"X-UStreamer-Expose-Begin-Time: %.06Lf" RN
|
||||||
|
"X-UStreamer-Expose-Cmp-Time: %.06Lf" RN
|
||||||
"X-UStreamer-Expose-End-Time: %.06Lf" RN
|
"X-UStreamer-Expose-End-Time: %.06Lf" RN
|
||||||
"X-UStreamer-Send-Time: %.06Lf" RN
|
"X-UStreamer-Send-Time: %.06Lf" RN
|
||||||
RN,
|
RN,
|
||||||
|
(EXPOSED(online) ? "true" : "false"),
|
||||||
|
client->fps,
|
||||||
EXPOSED(picture.grab_time),
|
EXPOSED(picture.grab_time),
|
||||||
EXPOSED(picture.encode_begin_time),
|
EXPOSED(picture.encode_begin_time),
|
||||||
EXPOSED(picture.encode_end_time),
|
EXPOSED(picture.encode_end_time),
|
||||||
EXPOSED(expose_begin_time),
|
EXPOSED(expose_begin_time),
|
||||||
|
EXPOSED(expose_cmp_time),
|
||||||
EXPOSED(expose_end_time),
|
EXPOSED(expose_end_time),
|
||||||
now_monotonic_ms()
|
now
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,19 +414,32 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _http_queue_send_stream(struct http_server_t *server, const bool updated) {
|
static void _http_queue_send_stream(struct http_server_t *server, const bool updated) {
|
||||||
struct stream_client_t *client;
|
|
||||||
struct evhttp_connection *conn;
|
struct evhttp_connection *conn;
|
||||||
struct bufferevent *buf_event;
|
struct bufferevent *buf_event;
|
||||||
|
long long now;
|
||||||
|
bool queued = false;
|
||||||
|
static unsigned queued_fps_accum = 0;
|
||||||
|
static long long queued_fps_second = 0;
|
||||||
|
|
||||||
for (client = server->run->stream_clients; client != NULL; client = client->next) {
|
for (struct stream_client_t *client = server->run->stream_clients; client != NULL; client = client->next) {
|
||||||
conn = evhttp_request_get_connection(client->request);
|
conn = evhttp_request_get_connection(client->request);
|
||||||
if (conn != NULL && (updated || client->need_first_frame)) {
|
if (conn != NULL && (updated || client->need_first_frame)) {
|
||||||
buf_event = evhttp_connection_get_bufferevent(conn);
|
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||||
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
|
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
|
||||||
bufferevent_enable(buf_event, EV_READ|EV_WRITE);
|
bufferevent_enable(buf_event, EV_READ|EV_WRITE);
|
||||||
client->need_first_frame = false;
|
client->need_first_frame = false;
|
||||||
|
queued = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (queued) {
|
||||||
|
if ((now = floor_ms(get_now_monotonic())) != queued_fps_second) {
|
||||||
|
server->run->exposed->queued_fps = queued_fps_accum;
|
||||||
|
queued_fps_accum = 0;
|
||||||
|
queued_fps_second = now;
|
||||||
|
}
|
||||||
|
queued_fps_accum += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_server) {
|
static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_server) {
|
||||||
@@ -423,6 +471,20 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (queue_send) {
|
if (queue_send) {
|
||||||
|
if (server->drop_same_frames) {
|
||||||
|
// Хром всегда показывает не новый пришедший фрейм, а предыдущий.
|
||||||
|
// При updated == false нужно еще один раз послать предыдущий фрейм
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=527446
|
||||||
|
|
||||||
|
static bool updated_prev = false;
|
||||||
|
bool updated_orig = updated;
|
||||||
|
|
||||||
|
if (updated_prev && !updated_orig) {
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
updated_prev = updated_orig;
|
||||||
|
}
|
||||||
|
|
||||||
_http_queue_send_stream(server, updated);
|
_http_queue_send_stream(server, updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,8 +497,8 @@ static bool _expose_new_picture(struct http_server_t *server) {
|
|||||||
# define EXPOSED(_next) server->run->exposed->_next
|
# define EXPOSED(_next) server->run->exposed->_next
|
||||||
|
|
||||||
assert(STREAM(picture.size) > 0);
|
assert(STREAM(picture.size) > 0);
|
||||||
EXPOSED(fps) = STREAM(fps);
|
EXPOSED(captured_fps) = STREAM(captured_fps);
|
||||||
EXPOSED(expose_begin_time) = now_monotonic_ms();
|
EXPOSED(expose_begin_time) = get_now_monotonic();
|
||||||
|
|
||||||
# define MEM_STREAM_TO_EXPOSED \
|
# define MEM_STREAM_TO_EXPOSED \
|
||||||
EXPOSED(picture.data), STREAM(picture.data), \
|
EXPOSED(picture.data), STREAM(picture.data), \
|
||||||
@@ -449,10 +511,20 @@ static bool _expose_new_picture(struct http_server_t *server) {
|
|||||||
&& EXPOSED(picture.size) == STREAM(picture.size)
|
&& EXPOSED(picture.size) == STREAM(picture.size)
|
||||||
&& !memcmp(MEM_STREAM_TO_EXPOSED)
|
&& !memcmp(MEM_STREAM_TO_EXPOSED)
|
||||||
) {
|
) {
|
||||||
LOG_PERF("HTTP: dropped same frame number %u", EXPOSED(dropped));
|
EXPOSED(expose_cmp_time) = get_now_monotonic();
|
||||||
|
EXPOSED(expose_end_time) = EXPOSED(expose_cmp_time);
|
||||||
|
LOG_PERF(
|
||||||
|
"HTTP: dropped same frame number %u; comparsion time = %.06Lf",
|
||||||
|
EXPOSED(dropped), EXPOSED(expose_cmp_time) - EXPOSED(expose_begin_time)
|
||||||
|
);
|
||||||
EXPOSED(dropped) += 1;
|
EXPOSED(dropped) += 1;
|
||||||
EXPOSED(expose_end_time) = now_monotonic_ms();
|
|
||||||
return false; // Not updated
|
return false; // Not updated
|
||||||
|
} else {
|
||||||
|
EXPOSED(expose_cmp_time) = get_now_monotonic();
|
||||||
|
LOG_PERF(
|
||||||
|
"HTTP: passed same frame check (frames are differ); comparsion time = %.06Lf",
|
||||||
|
EXPOSED(expose_cmp_time) - EXPOSED(expose_begin_time)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +547,8 @@ static bool _expose_new_picture(struct http_server_t *server) {
|
|||||||
EXPOSED(height) = STREAM(height);
|
EXPOSED(height) = STREAM(height);
|
||||||
EXPOSED(online) = true;
|
EXPOSED(online) = true;
|
||||||
EXPOSED(dropped) = 0;
|
EXPOSED(dropped) = 0;
|
||||||
EXPOSED(expose_end_time) = now_monotonic_ms();
|
EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time);
|
||||||
|
EXPOSED(expose_end_time) = get_now_monotonic();
|
||||||
|
|
||||||
# undef STREAM
|
# undef STREAM
|
||||||
# undef EXPOSED
|
# undef EXPOSED
|
||||||
@@ -485,7 +558,8 @@ static bool _expose_new_picture(struct http_server_t *server) {
|
|||||||
static bool _expose_blank_picture(struct http_server_t *server) {
|
static bool _expose_blank_picture(struct http_server_t *server) {
|
||||||
# define EXPOSED(_next) server->run->exposed->_next
|
# define EXPOSED(_next) server->run->exposed->_next
|
||||||
|
|
||||||
EXPOSED(expose_begin_time) = now_monotonic_ms();
|
EXPOSED(expose_begin_time) = get_now_monotonic();
|
||||||
|
EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time);
|
||||||
|
|
||||||
if (EXPOSED(online) || EXPOSED(picture.size) == 0) {
|
if (EXPOSED(online) || EXPOSED(picture.size) == 0) {
|
||||||
if (EXPOSED(picture.allocated) < BLANK_JPG_SIZE) {
|
if (EXPOSED(picture.allocated) < BLANK_JPG_SIZE) {
|
||||||
@@ -506,7 +580,7 @@ static bool _expose_blank_picture(struct http_server_t *server) {
|
|||||||
|
|
||||||
EXPOSED(width) = BLANK_JPG_WIDTH;
|
EXPOSED(width) = BLANK_JPG_WIDTH;
|
||||||
EXPOSED(height) = BLANK_JPG_HEIGHT;
|
EXPOSED(height) = BLANK_JPG_HEIGHT;
|
||||||
EXPOSED(fps) = 0;
|
EXPOSED(captured_fps) = 0;
|
||||||
EXPOSED(online) = false;
|
EXPOSED(online) = false;
|
||||||
goto updated;
|
goto updated;
|
||||||
}
|
}
|
||||||
@@ -514,13 +588,13 @@ static bool _expose_blank_picture(struct http_server_t *server) {
|
|||||||
if (EXPOSED(dropped) < server->run->drop_same_frames_blank) {
|
if (EXPOSED(dropped) < server->run->drop_same_frames_blank) {
|
||||||
LOG_PERF("HTTP: dropped same frame (BLANK) number %u", EXPOSED(dropped));
|
LOG_PERF("HTTP: dropped same frame (BLANK) number %u", EXPOSED(dropped));
|
||||||
EXPOSED(dropped) += 1;
|
EXPOSED(dropped) += 1;
|
||||||
EXPOSED(expose_end_time) = now_monotonic_ms();
|
EXPOSED(expose_end_time) = get_now_monotonic();
|
||||||
return false; // Not updated
|
return false; // Not updated
|
||||||
}
|
}
|
||||||
|
|
||||||
updated:
|
updated:
|
||||||
EXPOSED(dropped) = 0;
|
EXPOSED(dropped) = 0;
|
||||||
EXPOSED(expose_end_time) = now_monotonic_ms();
|
EXPOSED(expose_end_time) = get_now_monotonic();
|
||||||
return true; // Updated
|
return true; // Updated
|
||||||
|
|
||||||
# undef EXPOSED
|
# undef EXPOSED
|
||||||
|
|||||||
10
src/http.h
10
src/http.h
@@ -31,8 +31,12 @@
|
|||||||
struct stream_client_t {
|
struct stream_client_t {
|
||||||
struct http_server_t *server;
|
struct http_server_t *server;
|
||||||
struct evhttp_request *request;
|
struct evhttp_request *request;
|
||||||
|
char id[37]; // ex. "1b4e28ba-2fa1-11d2-883f-0016d3cca427" + "\0"
|
||||||
bool need_initial;
|
bool need_initial;
|
||||||
bool need_first_frame;
|
bool need_first_frame;
|
||||||
|
unsigned fps;
|
||||||
|
unsigned fps_accum;
|
||||||
|
long long fps_accum_second;
|
||||||
|
|
||||||
struct stream_client_t *prev;
|
struct stream_client_t *prev;
|
||||||
struct stream_client_t *next;
|
struct stream_client_t *next;
|
||||||
@@ -42,10 +46,12 @@ struct exposed_t {
|
|||||||
struct picture_t picture;
|
struct picture_t picture;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned fps;
|
unsigned captured_fps;
|
||||||
|
unsigned queued_fps;
|
||||||
bool online;
|
bool online;
|
||||||
unsigned dropped;
|
unsigned dropped;
|
||||||
long double expose_begin_time;
|
long double expose_begin_time;
|
||||||
|
long double expose_cmp_time;
|
||||||
long double expose_end_time;
|
long double expose_end_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,7 +70,7 @@ struct http_server_t {
|
|||||||
char *host;
|
char *host;
|
||||||
unsigned port;
|
unsigned port;
|
||||||
unsigned drop_same_frames;
|
unsigned drop_same_frames;
|
||||||
bool add_x_timings;
|
bool extra_stream_headers;
|
||||||
unsigned fake_width;
|
unsigned fake_width;
|
||||||
unsigned fake_height;
|
unsigned fake_height;
|
||||||
unsigned timeout;
|
unsigned timeout;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ pthread_mutex_t log_mutex;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_PRINTF_NOLOCK(_label, _msg, ...) { \
|
#define LOG_PRINTF_NOLOCK(_label, _msg, ...) { \
|
||||||
printf("-- " _label " [%.03Lf tid=%ld] -- " _msg "\n", now_monotonic_ms(), syscall(SYS_gettid), ##__VA_ARGS__); \
|
printf("-- " _label " [%.03Lf tid=%ld] -- " _msg "\n", get_now_monotonic(), syscall(SYS_gettid), ##__VA_ARGS__); \
|
||||||
fflush(stdout); \
|
fflush(stdout); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ pthread_mutex_t log_mutex;
|
|||||||
char _buf[1024] = ""; \
|
char _buf[1024] = ""; \
|
||||||
strerror_r(errno, _buf, 1024); \
|
strerror_r(errno, _buf, 1024); \
|
||||||
LOGGING_LOCK; \
|
LOGGING_LOCK; \
|
||||||
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", now_monotonic_ms(), syscall(SYS_gettid), ##__VA_ARGS__, _buf); \
|
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", get_now_monotonic(), syscall(SYS_gettid), ##__VA_ARGS__, _buf); \
|
||||||
fflush(stdout); \
|
fflush(stdout); \
|
||||||
LOGGING_UNLOCK; \
|
LOGGING_UNLOCK; \
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/main.c
29
src/main.c
@@ -25,7 +25,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdbool.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ static const struct option _long_opts[] = {
|
|||||||
{"host", required_argument, NULL, 's'},
|
{"host", required_argument, NULL, 's'},
|
||||||
{"port", required_argument, NULL, 'p'},
|
{"port", required_argument, NULL, 'p'},
|
||||||
{"drop-same-frames", required_argument, NULL, 'r'},
|
{"drop-same-frames", required_argument, NULL, 'r'},
|
||||||
{"add-x-timings", no_argument, NULL, 2000},
|
{"extra-stream-headers", no_argument, NULL, 2000},
|
||||||
{"fake-width", required_argument, NULL, 2001},
|
{"fake-width", required_argument, NULL, 2001},
|
||||||
{"fake-height", required_argument, NULL, 2002},
|
{"fake-height", required_argument, NULL, 2002},
|
||||||
{"server-timeout", required_argument, NULL, 2003},
|
{"server-timeout", required_argument, NULL, 2003},
|
||||||
@@ -74,13 +74,26 @@ static const struct option _long_opts[] = {
|
|||||||
{"debug", no_argument, NULL, 5002},
|
{"debug", no_argument, NULL, 5002},
|
||||||
{"log-level", required_argument, NULL, 5010},
|
{"log-level", required_argument, NULL, 5010},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{"version", no_argument, NULL, 6000},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void _version(bool nl) {
|
||||||
|
printf(VERSION);
|
||||||
|
# ifdef OMX_ENCODER
|
||||||
|
printf(" with OMX");
|
||||||
|
# endif
|
||||||
|
if (nl) {
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
|
static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_server_t *server) {
|
||||||
printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n");
|
printf("\nuStreamer - Lightweight and fast MJPG-HTTP streamer\n");
|
||||||
printf("===================================================\n\n");
|
printf("===================================================\n\n");
|
||||||
printf("Version: %s; license: GPLv3\n", VERSION);
|
printf("Version: ");
|
||||||
|
_version(false);
|
||||||
|
printf("; license: GPLv3\n");
|
||||||
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
|
printf("Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com>\n\n");
|
||||||
printf("Capturing options:\n");
|
printf("Capturing options:\n");
|
||||||
printf("------------------\n");
|
printf("------------------\n");
|
||||||
@@ -119,7 +132,7 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
|
|||||||
printf(" It can significantly reduce the outgoing traffic, but will increase\n");
|
printf(" It can significantly reduce the outgoing traffic, but will increase\n");
|
||||||
printf(" the CPU loading. Don't use this option with analog signal sources\n");
|
printf(" the CPU loading. Don't use this option with analog signal sources\n");
|
||||||
printf(" or webcams, it's useless. Default: disabled.\n\n");
|
printf(" or webcams, it's useless. Default: disabled.\n\n");
|
||||||
printf(" --add-x-timings -- Add X-UStreamer-*-Time headers to the /stream and /snapshot handles.\n");
|
printf(" --extra-stream-headers -- Add X-UStreamer-* headers to /stream handle (like /snapshot).\n");
|
||||||
printf(" Default: disabled.\n\n");
|
printf(" Default: disabled.\n\n");
|
||||||
printf(" --fake-width <N> -- Override image width for /ping. Default: disabled\n\n");
|
printf(" --fake-width <N> -- Override image width for /ping. Default: disabled\n\n");
|
||||||
printf(" --fake-height <N> -- Override image height for /ping. Default: disabled.\n\n");
|
printf(" --fake-height <N> -- Override image height for /ping. Default: disabled.\n\n");
|
||||||
@@ -182,7 +195,7 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
|||||||
case 's': OPT_SET(server->host, optarg);
|
case 's': OPT_SET(server->host, optarg);
|
||||||
case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535);
|
case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535);
|
||||||
case 'r': OPT_UNSIGNED(server->drop_same_frames, "--drop-same-frames", 0, 30);
|
case 'r': OPT_UNSIGNED(server->drop_same_frames, "--drop-same-frames", 0, 30);
|
||||||
case 2000: OPT_SET(server->add_x_timings, true);
|
case 2000: OPT_SET(server->extra_stream_headers, true);
|
||||||
case 2001: OPT_UNSIGNED(server->fake_width, "--fake-width", 0, 1920);
|
case 2001: OPT_UNSIGNED(server->fake_width, "--fake-width", 0, 1920);
|
||||||
case 2002: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200);
|
case 2002: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200);
|
||||||
case 2003: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);
|
case 2003: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);
|
||||||
@@ -191,8 +204,10 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e
|
|||||||
case 5001: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
|
case 5001: OPT_SET(log_level, LOG_LEVEL_VERBOSE);
|
||||||
case 5002: OPT_SET(log_level, LOG_LEVEL_DEBUG);
|
case 5002: OPT_SET(log_level, LOG_LEVEL_DEBUG);
|
||||||
case 5010: OPT_UNSIGNED(log_level, "--log-level", 0, 3);
|
case 5010: OPT_UNSIGNED(log_level, "--log-level", 0, 3);
|
||||||
|
case 'h': _help(dev, encoder, server); return 1;
|
||||||
|
case 6000: _version(true); return 1;
|
||||||
case 0: break;
|
case 0: break;
|
||||||
case 'h': default: _help(dev, encoder, server); return -1;
|
default: _help(dev, encoder, server); return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,5 +309,5 @@ int main(int argc, char *argv[]) {
|
|||||||
device_destroy(dev);
|
device_destroy(dev);
|
||||||
|
|
||||||
LOGGING_DESTROY;
|
LOGGING_DESTROY;
|
||||||
return abs(exit_code);
|
return (exit_code < 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
#define LOG_OMX_ERROR(_error, _msg, ...) { \
|
#define LOG_OMX_ERROR(_error, _msg, ...) { \
|
||||||
LOGGING_LOCK; \
|
LOGGING_LOCK; \
|
||||||
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", now_monotonic_ms(), \
|
printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", get_now_monotonic(), \
|
||||||
syscall(SYS_gettid), ##__VA_ARGS__, omx_error_to_string(_error)); \
|
syscall(SYS_gettid), ##__VA_ARGS__, omx_error_to_string(_error)); \
|
||||||
LOGGING_UNLOCK; \
|
LOGGING_UNLOCK; \
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/stream.c
23
src/stream.c
@@ -85,8 +85,8 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
unsigned frames_count = 0;
|
unsigned frames_count = 0;
|
||||||
long double grab_after = 0;
|
long double grab_after = 0;
|
||||||
unsigned fluency_passed = 0;
|
unsigned fluency_passed = 0;
|
||||||
unsigned fps = 0;
|
unsigned captured_fps_accum = 0;
|
||||||
long long fps_second = 0;
|
long long captured_fps_second = 0;
|
||||||
|
|
||||||
LOG_DEBUG("Allocation memory for stream picture ...");
|
LOG_DEBUG("Allocation memory for stream picture ...");
|
||||||
A_CALLOC(stream->picture.data, stream->dev->run->max_picture_size);
|
A_CALLOC(stream->picture.data, stream->dev->run->max_picture_size);
|
||||||
@@ -111,7 +111,7 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
free_worker_number = oldest_worker->ctx.number;
|
free_worker_number = oldest_worker->ctx.number;
|
||||||
oldest_worker = oldest_worker->order_next;
|
oldest_worker = oldest_worker->order_next;
|
||||||
|
|
||||||
LOG_PERF("##### ACCEPT : %u", free_worker_number);
|
LOG_PERF("##### Raw frame accepted; worker = %u", free_worker_number);
|
||||||
} else {
|
} else {
|
||||||
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
||||||
if (!pool.workers[number].has_job && (free_worker_number == -1
|
if (!pool.workers[number].has_job && (free_worker_number == -1
|
||||||
@@ -125,7 +125,7 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
assert(free_worker_number >= 0);
|
assert(free_worker_number >= 0);
|
||||||
assert(!pool.workers[free_worker_number].has_job);
|
assert(!pool.workers[free_worker_number].has_job);
|
||||||
|
|
||||||
LOG_PERF("----- DROP : %u", free_worker_number);
|
LOG_PERF("----- Raw frame dropped; worker = %u", free_worker_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream->dev->stop) {
|
if (stream->dev->stop) {
|
||||||
@@ -162,7 +162,8 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
LOG_DEBUG("Frame is ready");
|
LOG_DEBUG("Frame is ready");
|
||||||
|
|
||||||
struct v4l2_buffer buf_info;
|
struct v4l2_buffer buf_info;
|
||||||
long double now = now_monotonic_ms();
|
long double now = get_now_monotonic();
|
||||||
|
long long now_second = floor_ms(now);
|
||||||
|
|
||||||
if (_stream_grab_buffer(stream->dev, &buf_info) < 0) {
|
if (_stream_grab_buffer(stream->dev, &buf_info) < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -196,13 +197,13 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
}
|
}
|
||||||
fluency_passed = 0;
|
fluency_passed = 0;
|
||||||
|
|
||||||
if ((long long)now != fps_second) {
|
if (now_second != captured_fps_second) {
|
||||||
LOG_PERF("Oldest worker complete, encoding FPS = %u", fps);
|
stream->captured_fps = captured_fps_accum;
|
||||||
stream->fps = fps;
|
captured_fps_accum = 0;
|
||||||
fps = 0;
|
captured_fps_second = now_second;
|
||||||
fps_second = (long long)now;
|
LOG_PERF("Oldest worker complete, Captured-FPS = %u", stream->captured_fps);
|
||||||
}
|
}
|
||||||
fps += 1;
|
captured_fps_accum += 1;
|
||||||
|
|
||||||
long double fluency_delay = _stream_get_fluency_delay(stream->dev, &pool);
|
long double fluency_delay = _stream_get_fluency_delay(stream->dev, &pool);
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ struct stream_t {
|
|||||||
struct picture_t picture;
|
struct picture_t picture;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned fps;
|
unsigned captured_fps;
|
||||||
bool updated;
|
bool updated;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
struct device_t *dev;
|
struct device_t *dev;
|
||||||
|
|||||||
14
src/tools.h
14
src/tools.h
@@ -61,7 +61,11 @@ INLINE unsigned max_u(unsigned a, unsigned b) {
|
|||||||
return (a > b ? a : b);
|
return (a > b ? a : b);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void now_ms(clockid_t clk_id, time_t *sec, long *msec) {
|
INLINE long long floor_ms(long double now) {
|
||||||
|
return (long long) now - (now < (long long) now); // floor()
|
||||||
|
}
|
||||||
|
|
||||||
|
INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
|
||||||
struct timespec spec;
|
struct timespec spec;
|
||||||
|
|
||||||
assert(!clock_gettime(clk_id, &spec));
|
assert(!clock_gettime(clk_id, &spec));
|
||||||
@@ -74,18 +78,18 @@ INLINE void now_ms(clockid_t clk_id, time_t *sec, long *msec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE long double now_monotonic_ms(void) {
|
INLINE long double get_now_monotonic(void) {
|
||||||
time_t sec;
|
time_t sec;
|
||||||
long msec;
|
long msec;
|
||||||
|
|
||||||
now_ms(CLOCK_MONOTONIC_RAW, &sec, &msec);
|
get_now(CLOCK_MONOTONIC_RAW, &sec, &msec);
|
||||||
return (long double)sec + ((long double)msec) / 1000;
|
return (long double)sec + ((long double)msec) / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE long double now_real_ms(void) {
|
INLINE long double get_now_real(void) {
|
||||||
time_t sec;
|
time_t sec;
|
||||||
long msec;
|
long msec;
|
||||||
|
|
||||||
now_ms(CLOCK_REALTIME, &sec, &msec);
|
get_now(CLOCK_REALTIME, &sec, &msec);
|
||||||
return (long double)sec + ((long double)msec) / 1000;
|
return (long double)sec + ((long double)msec) / 1000;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user