Compare commits

...

5 Commits
v0.28 ... v0.30

Author SHA1 Message Date
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
Devaev Maxim
97b2183038 Bump version: 0.28 → 0.29 2018-11-05 04:39:31 +03:00
Devaev Maxim
2732482d36 refactoring, html fixes 2018-11-05 04:39:20 +03:00
Devaev Maxim
ca52f12378 stream fix for webkit 2018-11-05 04:09:13 +03:00
11 changed files with 102 additions and 47 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion] [bumpversion]
commit = True commit = True
tag = True tag = True
current_version = 0.28 current_version = 0.30
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}

View File

@@ -29,8 +29,8 @@ install: $(PROG)
regen: regen:
tools/make-jpeg-h.py src/data/blank.jpeg src/data/blank.h BLANK 640 480 tools/make-jpeg-h.py src/data/blank.jpeg src/data/blank_jpeg.h BLANK 640 480
tools/make-html-h.py src/data/index.html src/data/html_index.h HTML_INDEX_PAGE tools/make-html-h.py src/data/index.html src/data/index_html.h HTML_INDEX_PAGE
$(PROG): $(OBJECTS) $(PROG): $(OBJECTS)
@@ -57,7 +57,7 @@ push:
git push git push
git push --tags git push --tags
clean-all: clean
clean: clean:
rm -f src/*.o src/{jpeg,omx}/*.o vgcore.* $(PROG) rm -f src/*.o src/{jpeg,omx}/*.o vgcore.* $(PROG)
rm -rf pkg src/$(PROG)-* src/v*.tar.gz v*.tar.gz $(PROG)-*.pkg.tar.xz rm -rf pkg src/$(PROG)-* src/v*.tar.gz v*.tar.gz $(PROG)-*.pkg.tar.xz

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer pkgname=ustreamer
pkgver=0.28 pkgver=0.30
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"

View File

@@ -21,4 +21,4 @@
#pragma once #pragma once
#define VERSION "0.28" #define VERSION "0.30"

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset=\"utf-8\"> <meta charset="utf-8" />
<title>uStreamer</title> <title>uStreamer</title>
</head> </head>
@@ -11,29 +11,36 @@
<hr> <hr>
<ul> <ul>
<li> <li>
<a href=\"/ping\"><b>/ping</b></a><br> <a href="/ping"><b><samp>/ping</samp></b></a><br>
Get JSON structure with state of the server. Get JSON structure with state of the server.
</li> </li>
<br> <br>
<li> <li>
<a href=\"/snapshot\"><b>/snapshot</b></a><br> <a href="/snapshot"><b><samp>/snapshot</samp></b></a><br>
Get a current actual image from server. Get a current actual image from the server.
</li> </li>
<br> <br>
<li> <li>
<a href=\"/stream\"><b>/stream</b></a><br> <a href="/stream"><b><samp>/stream</samp></b></a><br>
Get a live stream. Query params:<br> Get a live stream. Query params:<br>
<br> <br>
<ul> <ul>
<li> <li>
<i>extra_headers=1</i><br> <b><samp>extra_headers=1</samp></b><br>
Add X-UStreamer-* headers to /stream handle (like on <a href=\"/snapshot\">/snapshot</a>). Add <samp>X-UStreamer-*</samp> headers to /stream handle (like on <a href="/snapshot"><samp>/snapshot</samp></a>).
</li> </li>
<br> <br>
<li> <li>
<i>advance_headers=1</i><br> <b><samp>advance_headers=1</samp></b><br>
Enable workaround for Chromium/Blink Enable workaround for Chromium/Blink
<a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=527446\">Bug #527446</a>. <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=527446">Bug #527446</a>.
</li>
<br>
<li>
<b><samp>dual_final_frames=1</samp></b><br>
Enable workaround for Safari/WebKit bug when using option <samp>--drop-same-frames</samp>.<br>
Without this option, when the frame series is completed, WebKit-based browsers<br>
renders the last one with a delay.
</li> </li>
</ul> </ul>
</li> </li>
@@ -41,6 +48,6 @@
</ul> </ul>
<br> <br>
<hr> <hr>
<a href=\"https://github.com/pi-kvm/ustreamer\">Sources &amp; docs</a> <a href="https://github.com/pi-kvm/ustreamer">Sources &amp; docs</a>
</body> </body>
</html> </html>

View File

@@ -29,7 +29,7 @@ const char *HTML_INDEX_PAGE = " \
\ \
<html> \ <html> \
<head> \ <head> \
<meta charset=\"utf-8\"> \ <meta charset=\"utf-8\" /> \
<title>uStreamer</title> \ <title>uStreamer</title> \
</head> \ </head> \
\ \
@@ -38,30 +38,37 @@ const char *HTML_INDEX_PAGE = " \
<hr> \ <hr> \
<ul> \ <ul> \
<li> \ <li> \
<a href=\"/ping\"><b>/ping</b></a><br> \ <a href=\"/ping\"><b><samp>/ping</samp></b></a><br> \
Get JSON structure with state of the server. \ Get JSON structure with state of the server. \
</li> \ </li> \
<br> \ <br> \
<li> \ <li> \
<a href=\"/snapshot\"><b>/snapshot</b></a><br> \ <a href=\"/snapshot\"><b><samp>/snapshot</samp></b></a><br> \
Get a current actual image from server. \ Get a current actual image from the server. \
</li> \ </li> \
<br> \ <br> \
<li> \ <li> \
<a href=\"/stream\"><b>/stream</b></a><br> \ <a href=\"/stream\"><b><samp>/stream</samp></b></a><br> \
Get a live stream. Query params:<br> \ Get a live stream. Query params:<br> \
<br> \ <br> \
<ul> \ <ul> \
<li> \ <li> \
<i>extra_headers=1</i><br> \ <b><samp>extra_headers=1</samp></b><br> \
Add X-UStreamer-* headers to /stream handle (like on <a href=\"/snapshot\">/snapshot</a>). \ Add <samp>X-UStreamer-*</samp> headers to /stream handle (like on <a href=\"/snapshot\"><samp>/snapshot</samp></a>). \
</li> \ </li> \
<br> \ <br> \
<li> \ <li> \
<i>advance_headers=1</i><br> \ <b><samp>advance_headers=1</samp></b><br> \
Enable workaround for Chromium/Blink \ Enable workaround for Chromium/Blink \
<a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=527446\">Bug #527446</a>. \ <a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=527446\">Bug #527446</a>. \
</li> \ </li> \
<br> \
<li> \
<b><samp>dual_final_frames=1</samp></b><br> \
Enable workaround for Safari/WebKit bug when using option <samp>--drop-same-frames</samp>.<br> \
Without this option, when the frame series is completed, WebKit-based browsers<br> \
renders the last one with a delay. \
</li> \
</ul> \ </ul> \
</li> \ </li> \
<br> \ <br> \

View File

@@ -43,8 +43,8 @@
#include "stream.h" #include "stream.h"
#include "http.h" #include "http.h"
#include "data/html_index.h" #include "data/index_html.h"
#include "data/blank.h" #include "data/blank_jpeg.h"
static bool _http_get_param_true(struct evkeyvalq *params, const char *key); static bool _http_get_param_true(struct evkeyvalq *params, const char *key);
@@ -58,7 +58,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_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_exposed_refresh(int fd, short event, void *v_server);
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 stream_updated, const bool picture_updated);
static bool _expose_new_picture(struct http_server_t *server); 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);
@@ -192,7 +192,7 @@ static void _http_callback_ping(struct evhttp_request *request, void *v_server)
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {", " \"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->online ? "true" : "false"), bool_to_string(server->run->exposed->online),
server->run->stream->encoder->quality, server->run->stream->encoder->quality,
server->run->exposed->captured_fps, server->run->exposed->captured_fps,
server->run->exposed->queued_fps, server->run->exposed->queued_fps,
@@ -200,10 +200,11 @@ static void _http_callback_ping(struct evhttp_request *request, void *v_server)
)); ));
for (struct stream_client_t * 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) {
assert(evbuffer_add_printf(buf, assert(evbuffer_add_printf(buf,
"\"%s\": {\"fps\": %u, \"advance_headers\": %s}%s", "\"%s\": {\"fps\": %u, \"advance_headers\": %s, \"dual_final_frames\": %s}%s",
client->id, client->id,
client->fps, client->fps,
(client->advance_headers ? "true" : "false"), bool_to_string(client->advance_headers),
bool_to_string(client->dual_final_frames),
(client->next ? ", " : "") (client->next ? ", " : "")
)); ));
} }
@@ -236,7 +237,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
ADD_TIME_HEADER("X-Timestamp", get_now_real()); ADD_TIME_HEADER("X-Timestamp", get_now_real());
ADD_HEADER("X-UStreamer-Online", (EXPOSED(online) ? "true" : "false")); ADD_HEADER("X-UStreamer-Online", bool_to_string(EXPOSED(online)));
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));
@@ -286,6 +287,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
evhttp_parse_query(evhttp_request_get_uri(request), &params); evhttp_parse_query(evhttp_request_get_uri(request), &params);
client->extra_headers = _http_get_param_true(&params, "extra_headers"); client->extra_headers = _http_get_param_true(&params, "extra_headers");
client->advance_headers = _http_get_param_true(&params, "advance_headers"); client->advance_headers = _http_get_param_true(&params, "advance_headers");
client->dual_final_frames = _http_get_param_true(&params, "dual_final_frames");
evhttp_clear_headers(&params); evhttp_clear_headers(&params);
uuid_generate(uuid); uuid_generate(uuid);
@@ -304,8 +306,14 @@ 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; id=%s; clients now: %u", "HTTP: Registered the new stream client: [%s]:%u; id=%s;"
client_addr, client_port, client->id, server->run->stream_clients_count " 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); buf_event = evhttp_connection_get_bufferevent(conn);
@@ -404,7 +412,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"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"), bool_to_string(EXPOSED(online)),
client->fps, client->fps,
EXPOSED(picture.grab_time), EXPOSED(picture.grab_time),
EXPOSED(picture.encode_begin_time), EXPOSED(picture.encode_begin_time),
@@ -470,7 +478,7 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
free(client); free(client);
} }
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 stream_updated, const bool picture_updated) {
struct evhttp_connection *conn; struct evhttp_connection *conn;
struct bufferevent *buf_event; struct bufferevent *buf_event;
long long now; long long now;
@@ -480,12 +488,32 @@ static void _http_queue_send_stream(struct http_server_t *server, const bool upd
for (struct stream_client_t *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) {
buf_event = evhttp_connection_get_bufferevent(conn); // Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client); // WebKit отрисовывает последний фрейм в серии с некоторой задержкой,
bufferevent_enable(buf_event, EV_READ|EV_WRITE); // и нужно послать два фрейма, чтобы серия была вовремя завершена.
client->need_first_frame = false; // Это похоже на баг Blink (см. _http_callback_stream_write() и advance_headers),
queued = true; // но фикс для него не лечит проблему вебкита. Такие дела.
bool dual_update = (
server->drop_same_frames
&& client->dual_final_frames
&& stream_updated
&& client->updated_prev
&& !picture_updated
);
if (dual_update || picture_updated || client->need_first_frame) {
buf_event = evhttp_connection_get_bufferevent(conn);
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
bufferevent_enable(buf_event, EV_READ|EV_WRITE);
client->need_first_frame = false;
client->updated_prev = (picture_updated || client->need_first_frame); // Игнорировать dual
queued = true;
} else if (stream_updated) { // Для dual
client->updated_prev = false;
}
} }
} }
@@ -501,7 +529,8 @@ static void _http_queue_send_stream(struct http_server_t *server, const bool upd
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) {
struct http_server_t *server = (struct http_server_t *)v_server; struct http_server_t *server = (struct http_server_t *)v_server;
bool updated = false; bool stream_updated = false;
bool picture_updated = false;
# define UNLOCK_STREAM \ # define UNLOCK_STREAM \
{ server->run->stream->updated = false; A_PTHREAD_M_UNLOCK(&server->run->stream->mutex); } { server->run->stream->updated = false; A_PTHREAD_M_UNLOCK(&server->run->stream->mutex); }
@@ -510,20 +539,22 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv
LOG_DEBUG("Refreshing HTTP exposed ..."); LOG_DEBUG("Refreshing HTTP exposed ...");
A_PTHREAD_M_LOCK(&server->run->stream->mutex); A_PTHREAD_M_LOCK(&server->run->stream->mutex);
if (server->run->stream->picture.size > 0) { // If online if (server->run->stream->picture.size > 0) { // If online
updated = _expose_new_picture(server); picture_updated = _expose_new_picture(server);
UNLOCK_STREAM; UNLOCK_STREAM;
} else { } else {
UNLOCK_STREAM; UNLOCK_STREAM;
updated = _expose_blank_picture(server); picture_updated = _expose_blank_picture(server);
} }
stream_updated = true;
} else if (!server->run->exposed->online) { } else if (!server->run->exposed->online) {
LOG_DEBUG("Refreshing HTTP exposed (BLANK) ..."); LOG_DEBUG("Refreshing HTTP exposed (BLANK) ...");
updated = _expose_blank_picture(server); picture_updated = _expose_blank_picture(server);
stream_updated = true;
} }
# undef UNLOCK_STREAM # undef UNLOCK_STREAM
_http_queue_send_stream(server, updated); _http_queue_send_stream(server, stream_updated, picture_updated);
} }
static bool _expose_new_picture(struct http_server_t *server) { static bool _expose_new_picture(struct http_server_t *server) {

View File

@@ -31,11 +31,15 @@
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 extra_headers; bool extra_headers;
bool advance_headers; bool advance_headers;
bool dual_final_frames;
char id[37]; // ex. "1b4e28ba-2fa1-11d2-883f-0016d3cca427" + "\0"
bool need_initial; bool need_initial;
bool need_first_frame; bool need_first_frame;
bool updated_prev;
unsigned fps; unsigned fps;
unsigned fps_accum; unsigned fps_accum;
long long fps_accum_second; long long fps_accum_second;

View File

@@ -23,6 +23,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <errno.h> #include <errno.h>
#include <math.h> #include <math.h>
#include <pthread.h> #include <pthread.h>
@@ -57,6 +58,10 @@
#define UNUSED __attribute__((unused)) #define UNUSED __attribute__((unused))
INLINE char *bool_to_string(const bool flag) {
return (flag ? "true" : "false");
}
INLINE unsigned max_u(unsigned a, unsigned b) { INLINE unsigned max_u(unsigned a, unsigned b) {
return (a > b ? a : b); return (a > b ? a : b);
} }

View File

@@ -37,6 +37,7 @@ def main():
text = html_file.read() text = html_file.read()
text = text.strip() text = text.strip()
text = text.replace("\"", "\\\"")
text = text.replace("%VERSION%", "\" VERSION \"") text = text.replace("%VERSION%", "\" VERSION \"")
text = textwrap.indent(text, "\t", (lambda line: True)) text = textwrap.indent(text, "\t", (lambda line: True))
text = "\n".join(("%s \\" if line.strip() else "%s\\") % (line) for line in text.split("\n")) text = "\n".join(("%s \\" if line.strip() else "%s\\") % (line) for line in text.split("\n"))