Compare commits

...

23 Commits
v4.3 ... v4.9

Author SHA1 Message Date
Maxim Devaev
030077fb47 Bump version: 4.8 → 4.9 2021-10-30 11:40:31 +03:00
Maxim Devaev
61ddff8b6e Merge pull request #129 from b-rad15/master
Change html links to relative not absolute
2021-10-30 11:27:20 +03:00
Bradley O'Connell
27fbfe149e changed html links to relative not absolute 2021-10-30 03:20:57 -04:00
Maxim Devaev
349f655cd9 Bump version: 4.7 → 4.8 2021-10-19 15:56:19 +03:00
Maxim Devaev
f5c0a15967 removed ansible link 2021-10-19 15:55:34 +03:00
Maxim Devaev
d822ae0890 Fixed #121: static inline 2021-10-19 15:47:42 +03:00
Maxim Devaev
2d82adb2ba Bump version: 4.6 → 4.7 2021-10-17 00:14:25 +03:00
Maxim Devaev
56b21274d1 ru doc, etc 2021-10-17 00:13:01 +03:00
Maxim Devaev
76329ba5a6 Fixed #128: added mjpg_streamer compatibility for --static 2021-10-17 00:08:13 +03:00
Maxim Devaev
4d5c5fffb4 Merge pull request #127 from tiny-pilot/nginx-doc
Document how to integrate uStreamer with nginx
2021-10-17 00:07:55 +03:00
Michael Lynch
29234c330b Document how to integrate uStreamer with nginx
This change documents a gotcha that can occur when clients deploy uStreamer behind an nginx proxy. By default, nginx buffers responses, which introduces latency into the video stream. Disabling the buffer eliminates this latency.
2021-10-14 10:02:42 -04:00
Maxim Devaev
690d291818 Bump version: 4.5 → 4.6 2021-09-27 23:14:24 +03:00
Maxim Devaev
8cfb3fc301 lint fix 2021-09-27 22:55:01 +03:00
Maxim Devaev
69ab3fa831 ustreamer-dump: count and interval 2021-09-27 22:54:08 +03:00
Maxim Devaev
79d6c76084 Bump version: 4.4 → 4.5 2021-08-13 11:02:34 +03:00
Maxim Devaev
f15d2fc194 fixed .dockerignore 2021-08-13 10:49:23 +03:00
Maxim Devaev
99aa4828c5 fixed .gitignore 2021-08-13 10:48:41 +03:00
Maxim Devaev
93969f487c Issue #115: Comment about rtp_wrap_h264() 2021-08-13 10:39:24 +03:00
Maxim Devaev
61407c6596 Issue #115: Avoid usage of uninitialized last_client_ts 2021-08-13 10:36:53 +03:00
Maxim Devaev
6c95a56f3a Merge pull request #119 from PCPartPicker/vcpr_h264_rtp_mark_fix
Fix h264 rtp packetization - the marked bit should only be set for th…
2021-08-05 01:46:22 +03:00
aggieNick02
283f53a961 Fix h264 rtp packetization - the marked bit should only be set for the very last packet of each access unit (frame) - previously it was being set for the last packet of each type 1-5 NALU, which is equivalent and correct if the frame contains only one type 1-5 NALU, but not if it contains more 2021-08-03 23:10:21 -05:00
Maxim Devaev
ef8fb8216e .editorconfig 2021-07-25 04:23:24 +03:00
Devaev Maxim
6293d54296 Bump version: 4.3 → 4.4 2021-06-10 19:32:24 +03:00
20 changed files with 190 additions and 61 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 4.3
current_version = 4.9
parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize =
{major}.{minor}

View File

@@ -4,3 +4,6 @@
# Allow source code
!Makefile
!src/**
!python/**
!janus/**
!man/**

10
.editorconfig Normal file
View File

@@ -0,0 +1,10 @@
root = true
[*]
end_of_file = lf
indent_style = tab
indent_size = 4
[*.{py,yaml}]
indent_style = space
indent_size = 4

14
.gitignore vendored
View File

@@ -2,16 +2,16 @@
/linters/.mypy_cache/
/pkg/arch/pkg/
/pkg/arch/src/
/pkg/arch/v*.tar.gz
/pkg/arch/ustreamer-*.pkg.tar.xz
/pkg/arch/ustreamer-*.pkg.tar.zst
/src/build/
/src/*.bin
/python/build/
/janus/build/
/ustreamer
/ustreamer-dump
/config.mk
/vgcore.*
/*.sock
/*.so
vgcore.*
*.sock
*.so
*.bin
*.pkg.tar.xz
*.pkg.tar.zst
*.tar.gz

View File

@@ -92,6 +92,21 @@ $ ./ustreamer --host :: -m jpeg --device-timeout=5 --buffers=3 -r 2592x1944
$ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
```
-----
# Integrations
## Nginx
When uStreamer is behind an Nginx proxy, it's buffering behavior introduces latency into the video stream. It's possible to disable Nginx's buffering to eliminate the additional latency:
```nginx
location /stream {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
proxy_pass http://ustreamer;
}
```
-----
# Tips & tricks for v4l2
v4l2 utilities provide the tools to manage USB webcam setting and information. Scripts can be use to make adjustments and run manually or with cron. Running in cron for example to change the exposure settings at certain times of day. The package is available in all Linux distributions and is usually called `v4l-utils`.
@@ -107,7 +122,6 @@ v4l2 utilities provide the tools to manage USB webcam setting and information. S
-----
# See also
* [Running uStreamer via systemd service](https://github.com/pikvm/ustreamer/issues/16).
* [uStreamer Ansible Role](https://github.com/mtlynch/ansible-role-ustreamer): Use [Ansible](https://docs.ansible.com/ansible/latest/index.html) to compile uStreamer and install it as a systemd service automatically.
-----
# License

View File

@@ -92,6 +92,21 @@ $ ./ustreamer --host :: -m jpeg --device-timeout=5 --buffers=3 -r 2592x1944
$ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
```
-----
# Интеграция
## Nginx
Если uStreamer находится на Nginx, то последний будет буферизировать поток и создавать дополнительную задержку в стриме. Чтобы задержки не было, буферизацию можно отключить:
```nginx
location /stream {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
proxy_pass http://ustreamer;
}
```
-----
# Утилиты V4L2
V4L2 предоставляет ряд официальных утилит для управления USB-вебкамерами и получения информации об устройствах. С их помощью можно писать всякие настроечные скрипты и запускать их по крону, если, например, вам требуется изменять настройки экспозиции в зависимости от времени суток. Пакет с этими утилитами доступен на всех дистрибутивах Linux и обычно называется `v4l-utils`.
@@ -107,7 +122,6 @@ V4L2 предоставляет ряд официальных утилит дл
-----
# Смотрите также
* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16).
* [uStreamer Ansible Role](https://github.com/mtlynch/ansible-role-ustreamer): Использование [Ansible](https://docs.ansible.com/ansible/latest/index.html) для сборки и установки стримера как systemd-сервиса.
-----
# Лицензия

View File

@@ -30,7 +30,7 @@
#define PRE 3 // Annex B prefix length
void _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, rtp_callback_f callback);
void _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, bool marked, rtp_callback_f callback);
static void _rtp_write_header(rtp_s *rtp, uint32_t pts, bool marked);
static ssize_t _find_annexb(const uint8_t *data, size_t size);
@@ -99,6 +99,9 @@ char *rtp_make_sdp(rtp_s *rtp) {
}
void rtp_wrap_h264(rtp_s *rtp, const frame_s *frame, rtp_callback_f callback) {
// There is a complicated logic here but everything works as it should:
// - https://github.com/pikvm/ustreamer/issues/115#issuecomment-893071775
assert(frame->format == V4L2_PIX_FMT_H264);
const uint32_t pts = get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
@@ -118,7 +121,7 @@ void rtp_wrap_h264(rtp_s *rtp, const frame_s *frame, rtp_callback_f callback) {
if (data[size - 1] == 0) { // Check for extra 00
--size;
}
_rtp_process_nalu(rtp, data, size, pts, callback);
_rtp_process_nalu(rtp, data, size, pts, false, callback);
}
last_offset = offset;
@@ -127,18 +130,16 @@ void rtp_wrap_h264(rtp_s *rtp, const frame_s *frame, rtp_callback_f callback) {
if (last_offset >= 0) {
const uint8_t *data = frame->data + last_offset + PRE;
size_t size = frame->used - last_offset - PRE;
_rtp_process_nalu(rtp, data, size, pts, callback);
_rtp_process_nalu(rtp, data, size, pts, true, callback);
}
}
void _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, rtp_callback_f callback) {
void _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, bool marked, rtp_callback_f callback) {
const unsigned ref_idc = (data[0] >> 5) & 3;
const unsigned type = data[0] & 0x1F;
bool marked = false; // M=1 if this is an access unit
frame_s *ps = NULL;
switch (type) {
case 1 ... 5: marked = true; break;
case 7: ps = rtp->sps; break;
case 8: ps = rtp->pps; break;
}

View File

@@ -30,6 +30,7 @@ disable =
too-many-ancestors,
no-else-return,
len-as-condition,
unspecified-encoding,
[REPORTS]
msg-template = {symbol} -- {path}:{line}({obj}): {msg}

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 4.3" "January 2021"
.TH USTREAMER-DUMP 1 "version 4.9" "January 2021"
.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file
@@ -38,6 +38,12 @@ Filename to dump output to. Use '-' for stdout. Default: just consume the sink.
.TP
.BR \-j ", " \-\-output-json
Format output as JSON. Required option --output. Default: disabled.
.TP
.BR \-c ", " \-\-count\ \fIN
Limit the number of frames. Default: 0 (infinite).
.TP
.BR \-i ", "\-\-interval\ \fIsec
Delay between reading frames (float). Default: 0.
.SS "Logging options"
.TP

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER 1 "version 4.3" "November 2020"
.TH USTREAMER 1 "version 4.9" "November 2020"
.SH NAME
ustreamer \- stream MJPG video from any V4L2 device to the network

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=4.3
pkgver=4.9
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=4.3
PKG_VERSION:=4.9
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

View File

@@ -8,7 +8,7 @@ from distutils.core import setup
if __name__ == "__main__":
setup(
name="ustreamer",
version="4.3",
version="4.9",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",

View File

@@ -25,6 +25,8 @@
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <float.h>
#include <getopt.h>
#include <errno.h>
#include <assert.h>
@@ -44,6 +46,8 @@ enum _OPT_VALUES {
_O_SINK_TIMEOUT = 't',
_O_OUTPUT = 'o',
_O_OUTPUT_JSON = 'j',
_O_COUNT = 'c',
_O_INTERVAL = 'i',
_O_HELP = 'h',
_O_VERSION = 'v',
@@ -61,6 +65,8 @@ static const struct option _LONG_OPTS[] = {
{"sink-timeout", required_argument, NULL, _O_SINK_TIMEOUT},
{"output", required_argument, NULL, _O_OUTPUT},
{"output-json", no_argument, NULL, _O_OUTPUT_JSON},
{"count", required_argument, NULL, _O_COUNT},
{"interval", required_argument, NULL, _O_INTERVAL},
{"log-level", required_argument, NULL, _O_LOG_LEVEL},
{"perf", no_argument, NULL, _O_PERF},
@@ -89,7 +95,10 @@ typedef struct {
static void _signal_handler(int signum);
static void _install_signal_handlers(void);
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx);
static int _dump_sink(
const char *sink_name, unsigned sink_timeout,
long long count, long double interval,
_output_context_s *ctx);
static void _help(FILE *fp);
@@ -102,6 +111,8 @@ int main(int argc, char *argv[]) {
unsigned sink_timeout = 1;
char *output_path = NULL;
bool output_json = false;
long long count = 0;
long double interval = 0;
# define OPT_SET(_dest, _value) { \
_dest = _value; \
@@ -118,6 +129,16 @@ int main(int argc, char *argv[]) {
break; \
}
# define OPT_LDOUBLE(_name, _dest, _min, _max) { \
errno = 0; char *_end = NULL; long double _tmp = strtold(optarg, &_end); \
if (errno || *_end || _tmp < _min || _tmp > _max) { \
printf("Invalid value for '%s=%s': min=%Lf, max=%Lf\n", _name, optarg, (long double)_min, (long double)_max); \
return 1; \
} \
_dest = _tmp; \
break; \
}
char short_opts[128];
build_short_options(_LONG_OPTS, short_opts, 128);
@@ -127,6 +148,8 @@ int main(int argc, char *argv[]) {
case _O_SINK_TIMEOUT: OPT_NUMBER("--sink-timeout", sink_timeout, 1, 60, 0);
case _O_OUTPUT: OPT_SET(output_path, optarg);
case _O_OUTPUT_JSON: OPT_SET(output_json, true);
case _O_COUNT: OPT_NUMBER("--count", count, 0, LLONG_MAX, 0);
case _O_INTERVAL: OPT_LDOUBLE("--interval", interval, 0, 60);
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
case _O_PERF: OPT_SET(us_log_level, LOG_LEVEL_PERF);
@@ -143,6 +166,7 @@ int main(int argc, char *argv[]) {
}
}
# undef OPT_LDOUBLE
# undef OPT_NUMBER
# undef OPT_SET
@@ -163,7 +187,7 @@ int main(int argc, char *argv[]) {
}
_install_signal_handlers();
int retval = abs(_dump_sink(sink_name, sink_timeout, &ctx));
int retval = abs(_dump_sink(sink_name, sink_timeout, count, interval, &ctx));
if (ctx.v_output && ctx.destroy) {
ctx.destroy(ctx.v_output);
}
@@ -201,7 +225,17 @@ static void _install_signal_handlers(void) {
assert(!sigaction(SIGPIPE, &sig_act, NULL));
}
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx) {
static int _dump_sink(
const char *sink_name, unsigned sink_timeout,
long long count, long double interval,
_output_context_s *ctx) {
if (count == 0) {
count = -1;
}
useconds_t interval_us = interval * 1000000;
frame_s *frame = frame_init();
memsink_s *sink = NULL;
@@ -243,6 +277,17 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_cont
if (ctx->v_output) {
ctx->write(ctx->v_output, frame);
}
if (count >= 0) {
--count;
if (count <= 0) {
break;
}
}
if (interval_us > 0) {
usleep(interval_us);
}
} else if (error == -2) {
usleep(1000);
} else {
@@ -282,6 +327,8 @@ static void _help(FILE *fp) {
SAY(" -t|--sink-timeout <sec> ─ Timeout for the upcoming frame. Default: 1.\n");
SAY(" -o|--output <filename> ─── Filename to dump output to. Use '-' for stdout. Default: just consume the sink.\n");
SAY(" -j|--output-json ──────── Format output as JSON. Required option --output. Default: disabled.\n");
SAY(" -c|--count <N> ───────── Limit the number of frames. Default: 0 (infinite).\n");
SAY(" -i|--interval <sec> ───── Delay between reading frames (float). Default: 0.\n");
SAY("Logging options:");
SAY("════════════════");
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");

View File

@@ -23,7 +23,7 @@
#pragma once
#define VERSION_MAJOR 4
#define VERSION_MINOR 3
#define VERSION_MINOR 9
#define MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define MAKE_VERSION1(_major, _minor) MAKE_VERSION2(_major, _minor)

View File

@@ -68,7 +68,7 @@ typedef struct {
_dest->encode_end_ts = _src->encode_end_ts; \
}
inline void frame_copy_meta(const frame_s *src, frame_s *dest) {
static inline void frame_copy_meta(const frame_s *src, frame_s *dest) {
FRAME_COPY_META(src, dest);
}
@@ -97,6 +97,6 @@ unsigned frame_get_padding(const frame_s *frame);
const char *fourcc_to_string(unsigned format, char *buf, size_t size);
inline bool is_jpeg(unsigned format) {
static inline bool is_jpeg(unsigned format) {
return (format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
}

View File

@@ -87,7 +87,11 @@ void memsink_destroy(memsink_s *sink) {
}
bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
// Возвращает true, если есть клиенты ИЛИ изменились метаданные
// Return true (the need to write to memsink) on any of these conditions:
// - EWOULDBLOCK - we have an active client;
// - Incorrect magic or version - need to first write;
// - We have some active clients by last_client_ts;
// - Frame meta differs (like size, format, but not timestamp).
assert(sink->server);
@@ -100,15 +104,18 @@ bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
return false;
}
atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic()));
if (sink->mem->magic != MEMSINK_MAGIC || sink->mem->version != MEMSINK_VERSION) {
return true;
}
bool retval = (atomic_load(&sink->has_clients) || !FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));
bool has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
atomic_store(&sink->has_clients, has_clients);
if (flock(sink->fd, LOCK_UN) < 0) {
LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
return false;
}
return retval;
return (has_clients || !FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
}
int memsink_server_put(memsink_s *sink, const frame_s *frame) {

View File

@@ -12,30 +12,30 @@
<hr>
<ul>
<li>
<a href="/state"><b>/state</b></a><br>
<a href="state"><b>/state</b></a><br>
Get JSON structure with the state of the server.
</li>
<br>
<li>
<a href="/snapshot"><b>/snapshot</b></a><br>
<a href="snapshot"><b>/snapshot</b></a><br>
Get a current actual image from the server.
</li>
<br>
<li>
<a href="/stream"><b>/stream</b></a><br>
<a href="stream"><b>/stream</b></a><br>
Get a live stream. Query params:<br>
<br>
<ul>
<li>
<b>key=abc123</b><br>
The user-defined key, which is part of cookie <i>stream_client</i>, which allows<br>
the stream client to determine its identifier and view statistics using <a href="/state">/state</a>.
the stream client to determine its identifier and view statistics using <a href="state">/state</a>.
</li>
<br>
<li>
<b>extra_headers=1</b><br>
Add <i>X-UStreamer-*</i> headers to the <a href="/stream">/stream</a> handle
(like with the <a href="/snapshot">/snapshot</a>).
Add <i>X-UStreamer-*</i> headers to the <a href="stream">/stream</a> handle
(like with the <a href="snapshot">/snapshot</a>).
</li>
<br>
<li>
@@ -62,9 +62,9 @@
The mjpg-streamer compatibility layer:<br>
<br>
<ul>
<li><a href="/?action=snapshot">/?action=snapshot</a> as alias to the <a href="/snapshot">/snapshot</a>.</li>
<li><a href="?action=snapshot">/?action=snapshot</a> as alias to the <a href="snapshot">/snapshot</a>.</li>
<br>
<li><a href="/?action=stream">/?action=stream</a> as alias to the <a href="/stream">/stream</a>.</li>
<li><a href="?action=stream">/?action=stream</a> as alias to the <a href="stream">/stream</a>.</li>
</ul>
</li>
</ul>

View File

@@ -37,30 +37,30 @@ const char *const HTML_INDEX_PAGE = " \
<hr> \
<ul> \
<li> \
<a href=\"/state\"><b>/state</b></a><br> \
<a href=\"state\"><b>/state</b></a><br> \
Get JSON structure with the state of the server. \
</li> \
<br> \
<li> \
<a href=\"/snapshot\"><b>/snapshot</b></a><br> \
<a href=\"snapshot\"><b>/snapshot</b></a><br> \
Get a current actual image from the server. \
</li> \
<br> \
<li> \
<a href=\"/stream\"><b>/stream</b></a><br> \
<a href=\"stream\"><b>/stream</b></a><br> \
Get a live stream. Query params:<br> \
<br> \
<ul> \
<li> \
<b>key=abc123</b><br> \
The user-defined key, which is part of cookie <i>stream_client</i>, which allows<br> \
the stream client to determine its identifier and view statistics using <a href=\"/state\">/state</a>. \
the stream client to determine its identifier and view statistics using <a href=\"state\">/state</a>. \
</li> \
<br> \
<li> \
<b>extra_headers=1</b><br> \
Add <i>X-UStreamer-*</i> headers to the <a href=\"/stream\">/stream</a> handle \
(like with the <a href=\"/snapshot\">/snapshot</a>). \
Add <i>X-UStreamer-*</i> headers to the <a href=\"stream\">/stream</a> handle \
(like with the <a href=\"snapshot\">/snapshot</a>). \
</li> \
<br> \
<li> \
@@ -87,9 +87,9 @@ const char *const HTML_INDEX_PAGE = " \
The mjpg-streamer compatibility layer:<br> \
<br> \
<ul> \
<li><a href=\"/?action=snapshot\">/?action=snapshot</a> as alias to the <a href=\"/snapshot\">/snapshot</a>.</li> \
<li><a href=\"?action=snapshot\">/?action=snapshot</a> as alias to the <a href=\"snapshot\">/snapshot</a>.</li> \
<br> \
<li><a href=\"/?action=stream\">/?action=stream</a> as alias to the <a href=\"/stream\">/stream</a>.</li> \
<li><a href=\"?action=stream\">/?action=stream</a> as alias to the <a href=\"stream\">/stream</a>.</li> \
</ul> \
</li> \
</ul> \

View File

@@ -25,6 +25,8 @@
static int _http_preprocess_request(struct evhttp_request *request, server_s *server);
static int _http_check_run_compat_action(struct evhttp_request *request, void *v_server);
static void _http_callback_root(struct evhttp_request *request, void *v_server);
static void _http_callback_static(struct evhttp_request *request, void *v_server);
static void _http_callback_state(struct evhttp_request *request, void *v_server);
@@ -220,41 +222,63 @@ static int _http_preprocess_request(struct evhttp_request *request, server_s *se
} \
}
static void _http_callback_root(struct evhttp_request *request, void *v_server) {
server_s *server = (server_s *)v_server;
static int _http_check_run_compat_action(struct evhttp_request *request, void *v_server) {
// MJPG-Streamer compatibility layer
PREPROCESS_REQUEST;
struct evkeyvalq params;
int error = 0;
struct evkeyvalq params; // For mjpg-streamer compatibility
evhttp_parse_query(evhttp_request_get_uri(request), &params);
const char *action = evhttp_find_header(&params, "action");
if (action && !strcmp(action, "snapshot")) {
_http_callback_snapshot(request, v_server);
goto ok;
} else if (action && !strcmp(action, "stream")) {
_http_callback_stream(request, v_server);
} else {
struct evbuffer *buf;
assert((buf = evbuffer_new()));
assert(evbuffer_add_printf(buf, "%s", HTML_INDEX_PAGE));
ADD_HEADER("Content-Type", "text/html");
evhttp_send_reply(request, HTTP_OK, "OK", buf);
evbuffer_free(buf);
goto ok;
}
evhttp_clear_headers(&params);
error = -1;
ok:
evhttp_clear_headers(&params);
return error;
}
#define COMPAT_REQUEST { \
if (_http_check_run_compat_action(request, v_server) == 0) { \
return; \
} \
}
static void _http_callback_root(struct evhttp_request *request, void *v_server) {
server_s *server = (server_s *)v_server;
PREPROCESS_REQUEST;
COMPAT_REQUEST;
struct evbuffer *buf;
assert((buf = evbuffer_new()));
assert(evbuffer_add_printf(buf, "%s", HTML_INDEX_PAGE));
ADD_HEADER("Content-Type", "text/html");
evhttp_send_reply(request, HTTP_OK, "OK", buf);
evbuffer_free(buf);
}
static void _http_callback_static(struct evhttp_request *request, void *v_server) {
server_s *server = (server_s *)v_server;
PREPROCESS_REQUEST;
COMPAT_REQUEST;
struct evbuffer *buf = NULL;
struct evhttp_uri *uri = NULL;
char *decoded_path = NULL;
char *static_path = NULL;
int fd = -1;
PREPROCESS_REQUEST;
{
char *uri_path;
@@ -324,6 +348,8 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
}
}
#undef COMPAT_REQUEST
static void _http_callback_state(struct evhttp_request *request, void *v_server) {
server_s *server = (server_s *)v_server;