mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 04:06:30 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bc4afca9d | ||
|
|
f43afababa | ||
|
|
1b2de09438 | ||
|
|
b0c54b18a5 | ||
|
|
f8e26d785f | ||
|
|
28563abdbc | ||
|
|
f1a869a215 | ||
|
|
9778a805ca | ||
|
|
a008dcf99d | ||
|
|
71c64e668d | ||
|
|
d9b91a1d5f | ||
|
|
d682a1c173 | ||
|
|
ba03333623 | ||
|
|
c7e6e5e006 | ||
|
|
45b1e2f285 | ||
|
|
d9bbd8a74d |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 2.0
|
current_version = 2.2
|
||||||
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}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
| Option to serve files<br>with a built-in HTTP server |  Yes |  Regular files only |
|
| Option to serve files<br>with a built-in HTTP server |  Yes |  Regular files only |
|
||||||
| Signaling about the stream state<br>on GPIO using [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) |  Yes |  No |
|
| Signaling about the stream state<br>on GPIO using [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) |  Yes |  No |
|
||||||
| Access to webcam controls (focus, servos)<br>and settings such as brightness via HTTP |  No |  Yes |
|
| Access to webcam controls (focus, servos)<br>and settings such as brightness via HTTP |  No |  Yes |
|
||||||
|
| Compatibility with mjpg-streamer's API |  Yes | :) |
|
||||||
|
|
||||||
Footnotes:
|
Footnotes:
|
||||||
* ```1``` Long before µStreamer, I made a [patch](https://github.com/jacksonliam/mjpg-streamer/pull/164) to add DV-timings support to mjpg-streamer and to keep it from hanging up no device disconnection. Alas, the patch is far from perfect and I can't guarantee it will work every time - mjpg-streamer's source code is very complicated and its structure is hard to understand. With this in mind, along with needing multithreading and JPEG hardware acceleration in the future, I decided to make my own stream server from scratch instead of supporting legacy code.
|
* ```1``` Long before µStreamer, I made a [patch](https://github.com/jacksonliam/mjpg-streamer/pull/164) to add DV-timings support to mjpg-streamer and to keep it from hanging up no device disconnection. Alas, the patch is far from perfect and I can't guarantee it will work every time - mjpg-streamer's source code is very complicated and its structure is hard to understand. With this in mind, along with needing multithreading and JPEG hardware acceleration in the future, I decided to make my own stream server from scratch instead of supporting legacy code.
|
||||||
@@ -38,6 +39,7 @@ You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support,
|
|||||||
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
||||||
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Add `libraspberrypi-dev` for `WITH_OMX=1` and `libgpiod` for `WITH_GPIO=1`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Add `libraspberrypi-dev` for `WITH_OMX=1` and `libgpiod` for `WITH_GPIO=1`.
|
||||||
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
||||||
|
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
||||||
|
|
||||||
On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
|
On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
| Возможность сервить файлы встроенным<br>HTTP-сервером |  Есть |  Нет каталогов |
|
| Возможность сервить файлы встроенным<br>HTTP-сервером |  Есть |  Нет каталогов |
|
||||||
| Вывод сигналов о состоянии стрима на GPIO<br>с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) |  Есть |  Нет |
|
| Вывод сигналов о состоянии стрима на GPIO<br>с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) |  Есть |  Нет |
|
||||||
| Поддержка контролов веб-камер (фокус,<br> движение сервами) и всяких настроек,<br> типа яркости, через HTTP |  Нет |  Есть |
|
| Поддержка контролов веб-камер (фокус,<br> движение сервами) и всяких настроек,<br> типа яркости, через HTTP |  Нет |  Есть |
|
||||||
|
| Совместимость с API mjpg-streamer'а |  Есть | :) |
|
||||||
|
|
||||||
Сносочки:
|
Сносочки:
|
||||||
* ```1``` Еще до написания µStreamer, я запилил [патч](https://github.com/jacksonliam/mjpg-streamer/pull/164), добавляющий в mjpg-streamer поддержку DV-таймингов и предотвращающий его зависание при отключении устройства. Однако патч, увы, далек от совершенства и я не гарантирую его стопроцентную работоспособность, поскольку код mjpg-streamer чрезвычайно запутан и очень плохо структурирован. Учитывая это, а также то, что в дальнейшем мне потребовались многопоточность и аппаратное кодирование JPEG, было принято решение написать свой стрим-сервер с нуля, чтобы не тратить силы на поддержку лишнего легаси.
|
* ```1``` Еще до написания µStreamer, я запилил [патч](https://github.com/jacksonliam/mjpg-streamer/pull/164), добавляющий в mjpg-streamer поддержку DV-таймингов и предотвращающий его зависание при отключении устройства. Однако патч, увы, далек от совершенства и я не гарантирую его стопроцентную работоспособность, поскольку код mjpg-streamer чрезвычайно запутан и очень плохо структурирован. Учитывая это, а также то, что в дальнейшем мне потребовались многопоточность и аппаратное кодирование JPEG, было принято решение написать свой стрим-сервер с нуля, чтобы не тратить силы на поддержку лишнего легаси.
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
|
||||||
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Добавьте `libraspberrypi-dev` для сборки с `WITH_OMX=1` и `libgpiod` для `WITH_GPIO=1`.
|
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev uuid-dev libbsd-dev`. Добавьте `libraspberrypi-dev` для сборки с `WITH_OMX=1` и `libgpiod` для `WITH_GPIO=1`.
|
||||||
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
* Debian: `sudo apt install build-essential libevent-dev libjpeg62-turbo-dev uuid-dev libbsd-dev`.
|
||||||
|
* Ubuntu 20.04 x86_64: `sudo apt install build-essential libevent-dev libjpeg62-dev uuid-dev libbsd-dev make gcc libjpeg8 libjpeg-turbo8 libuuid1 libbsd0`.
|
||||||
|
|
||||||
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ RUN pacman -Syu --noconfirm \
|
|||||||
python-pip \
|
python-pip \
|
||||||
python-tox \
|
python-tox \
|
||||||
cppcheck \
|
cppcheck \
|
||||||
|
npm \
|
||||||
&& (pacman -Sc --noconfirm || true)
|
&& (pacman -Sc --noconfirm || true)
|
||||||
|
|
||||||
|
RUN npm install htmlhint -g
|
||||||
|
|
||||||
CMD /bin/bash
|
CMD /bin/bash
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = cppcheck, flake8, pylint, mypy, vulture
|
envlist = cppcheck, flake8, pylint, mypy, vulture, htmlhint
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
@@ -44,3 +44,7 @@ whitelist_externals = bash
|
|||||||
commands = bash -c 'vulture tools/*.py'
|
commands = bash -c 'vulture tools/*.py'
|
||||||
deps =
|
deps =
|
||||||
vulture
|
vulture
|
||||||
|
|
||||||
|
[testenv:htmlhint]
|
||||||
|
whitelist_externals = htmlhint
|
||||||
|
commands = htmlhint src/http/data/*.html
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=2.0
|
pkgver=2.2
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
||||||
url="https://github.com/pikvm/ustreamer"
|
url="https://github.com/pikvm/ustreamer"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=2.0
|
PKG_VERSION:=2.2
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef VERSION
|
#ifndef VERSION
|
||||||
# define VERSION "2.0"
|
# define VERSION "2.2"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ static void _device_open_hw_fps(struct device_t *dev) {
|
|||||||
LOG_DEBUG("Calling ioctl(VIDIOC_G_PARM) ...");
|
LOG_DEBUG("Calling ioctl(VIDIOC_G_PARM) ...");
|
||||||
if (xioctl(dev->run->fd, VIDIOC_G_PARM, &setfps) < 0) {
|
if (xioctl(dev->run->fd, VIDIOC_G_PARM, &setfps) < 0) {
|
||||||
if (errno == ENOTTY) { // Quiet message for Auvidea B101
|
if (errno == ENOTTY) { // Quiet message for Auvidea B101
|
||||||
LOG_INFO("Quierying HW FPS changing is not supported");
|
LOG_INFO("Querying HW FPS changing is not supported");
|
||||||
} else {
|
} else {
|
||||||
LOG_PERROR("Unable to query HW FPS changing");
|
LOG_PERROR("Unable to query HW FPS changing");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "encoder.h"
|
#include "encoder.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -51,6 +52,7 @@ static const OMX_U32 _OUTPUT_PORT = 341;
|
|||||||
static int _i_omx = 0;
|
static int _i_omx = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static int _vcos_semwait(VCOS_SEMAPHORE_T *sem);
|
||||||
static int _omx_init_component(struct omx_encoder_t *omx);
|
static int _omx_init_component(struct omx_encoder_t *omx);
|
||||||
static int _omx_init_disable_ports(struct omx_encoder_t *omx);
|
static int _omx_init_disable_ports(struct omx_encoder_t *omx);
|
||||||
static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev);
|
static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev);
|
||||||
@@ -180,7 +182,6 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
|||||||
# define OUT(_next) omx->output_buffer->_next
|
# define OUT(_next) omx->output_buffer->_next
|
||||||
|
|
||||||
OMX_ERRORTYPE error;
|
OMX_ERRORTYPE error;
|
||||||
VCOS_STATUS_T sem_status;
|
|
||||||
size_t slice_size = (IN(nAllocLen) < HW_BUFFER(used) ? IN(nAllocLen) : HW_BUFFER(used));
|
size_t slice_size = (IN(nAllocLen) < HW_BUFFER(used) ? IN(nAllocLen) : HW_BUFFER(used));
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
||||||
@@ -237,12 +238,8 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vcos_semaphore_wait(&omx->handler_sem);
|
if (_vcos_semwait(&omx->handler_sem) != 0) {
|
||||||
switch (sem_status = vcos_semaphore_wait_timeout(&omx->handler_sem, 3000)) {
|
return -1;
|
||||||
case VCOS_SUCCESS: break;
|
|
||||||
case VCOS_EAGAIN: LOG_ERROR("Can't wait VCOS semaphore: EAGAIN (timeout)"); return -1;
|
|
||||||
case VCOS_EINVAL: LOG_ERROR("Can't wait VCOS semaphore: EINTVAL"); return -1;
|
|
||||||
default: LOG_ERROR("Can't wait VCOS semaphore: %d", sem_status); return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +249,40 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _vcos_semwait(VCOS_SEMAPHORE_T *sem) {
|
||||||
|
// vcos_semaphore_wait() can wait infinite
|
||||||
|
// vcos_semaphore_wait_timeout() is broken by design:
|
||||||
|
// - https://github.com/pikvm/ustreamer/issues/56
|
||||||
|
// - https://github.com/raspberrypi/userland/issues/658
|
||||||
|
// CFG_OMX_SEMWAIT_TIMEOUT is ugly busyloop
|
||||||
|
// Три стула.
|
||||||
|
|
||||||
|
# ifdef CFG_OMX_SEMWAIT_TIMEOUT
|
||||||
|
long double deadline_ts = get_now_monotonic() + (long double)CFG_OMX_SEMWAIT_TIMEOUT; // Seconds
|
||||||
|
VCOS_STATUS_T sem_status;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
sem_status = vcos_semaphore_trywait(sem);
|
||||||
|
if (sem_status == VCOS_SUCCESS) {
|
||||||
|
return 0;
|
||||||
|
} else if (sem_status != VCOS_EAGAIN || get_now_monotonic() > deadline_ts) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
switch (sem_status) {
|
||||||
|
case VCOS_EAGAIN: LOG_ERROR("Can't wait VCOS semaphore: EAGAIN (timeout)"); break;
|
||||||
|
case VCOS_EINVAL: LOG_ERROR("Can't wait VCOS semaphore: EINVAL"); break;
|
||||||
|
default: LOG_ERROR("Can't wait VCOS semaphore: %d", sem_status); break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
# else
|
||||||
|
return (vcos_semaphore_wait(sem) == VCOS_SUCCESS ? 0 : -1);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
static int _omx_init_component(struct omx_encoder_t *omx) {
|
static int _omx_init_component(struct omx_encoder_t *omx) {
|
||||||
OMX_ERRORTYPE error;
|
OMX_ERRORTYPE error;
|
||||||
|
|
||||||
@@ -433,32 +464,32 @@ static int _omx_setup_output(struct omx_encoder_t *omx, unsigned quality) {
|
|||||||
|
|
||||||
static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) {
|
static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) {
|
||||||
OMX_ERRORTYPE error;
|
OMX_ERRORTYPE error;
|
||||||
int retcode = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (omx->i_output_port_enabled) {
|
if (omx->i_output_port_enabled) {
|
||||||
retcode -= component_disable_port(&omx->encoder, _OUTPUT_PORT);
|
retval -= component_disable_port(&omx->encoder, _OUTPUT_PORT);
|
||||||
omx->i_output_port_enabled = false;
|
omx->i_output_port_enabled = false;
|
||||||
}
|
}
|
||||||
if (omx->i_input_port_enabled) {
|
if (omx->i_input_port_enabled) {
|
||||||
retcode -= component_disable_port(&omx->encoder, _INPUT_PORT);
|
retval -= component_disable_port(&omx->encoder, _INPUT_PORT);
|
||||||
omx->i_input_port_enabled = false;
|
omx->i_input_port_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (omx->input_buffer) {
|
if (omx->input_buffer) {
|
||||||
if ((error = OMX_FreeBuffer(omx->encoder, _INPUT_PORT, omx->input_buffer)) != OMX_ErrorNone) {
|
if ((error = OMX_FreeBuffer(omx->encoder, _INPUT_PORT, omx->input_buffer)) != OMX_ErrorNone) {
|
||||||
LOG_ERROR_OMX(error, "Can't free OMX JPEG input buffer");
|
LOG_ERROR_OMX(error, "Can't free OMX JPEG input buffer");
|
||||||
// retcode -= 1;
|
// retval -= 1;
|
||||||
}
|
}
|
||||||
omx->input_buffer = NULL;
|
omx->input_buffer = NULL;
|
||||||
}
|
}
|
||||||
if (omx->output_buffer) {
|
if (omx->output_buffer) {
|
||||||
if ((error = OMX_FreeBuffer(omx->encoder, _OUTPUT_PORT, omx->output_buffer)) != OMX_ErrorNone) {
|
if ((error = OMX_FreeBuffer(omx->encoder, _OUTPUT_PORT, omx->output_buffer)) != OMX_ErrorNone) {
|
||||||
LOG_ERROR_OMX(error, "Can't free OMX JPEG output buffer");
|
LOG_ERROR_OMX(error, "Can't free OMX JPEG output buffer");
|
||||||
// retcode -= 1;
|
// retval -= 1;
|
||||||
}
|
}
|
||||||
omx->output_buffer = NULL;
|
omx->output_buffer = NULL;
|
||||||
}
|
}
|
||||||
return retcode;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static OMX_ERRORTYPE _omx_event_handler(
|
static OMX_ERRORTYPE _omx_event_handler(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>uStreamer</title>
|
<title>uStreamer</title>
|
||||||
|
<style>body {font-family: monospace;}</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -11,46 +12,56 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/state"><b><samp>/state</samp></b></a><br>
|
<a href="/state"><b>/state</b></a><br>
|
||||||
Get JSON structure with state of the server.
|
Get JSON structure with the state of the server.
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
<li>
|
<li>
|
||||||
<a href="/snapshot"><b><samp>/snapshot</samp></b></a><br>
|
<a href="/snapshot"><b>/snapshot</b></a><br>
|
||||||
Get a current actual image from the server.
|
Get a current actual image from the server.
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
<li>
|
<li>
|
||||||
<a href="/stream"><b><samp>/stream</samp></b></a><br>
|
<a href="/stream"><b>/stream</b></a><br>
|
||||||
Get a live stream. Query params:<br>
|
Get a live stream. Query params:<br>
|
||||||
<br>
|
<br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<b><samp>key=abc123</samp></b><br>
|
<b>key=abc123</b><br>
|
||||||
User-defined key, which is part of cookie <samp>stream_client</samp>, which allows<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"><samp>/state</samp></a>.
|
the stream client to determine its identifier and view statistics using <a href="/state">/state</a>.
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
<li>
|
<li>
|
||||||
<b><samp>extra_headers=1</samp></b><br>
|
<b>extra_headers=1</b><br>
|
||||||
Add <samp>X-UStreamer-*</samp> headers to /stream handle (like on <a href="/snapshot"><samp>/snapshot</samp></a>).
|
Add <i>X-UStreamer-*</i> headers to the <a href="/stream">/stream</a> handle
|
||||||
|
(like with the <a href="/snapshot">/snapshot</a>).
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
<li>
|
<li>
|
||||||
<b><samp>advance_headers=1</samp></b><br>
|
<b>advance_headers=1</b><br>
|
||||||
Enable workaround for Chromium/Blink
|
Enable workaround for the Chromium/Blink bug
|
||||||
<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">#527446</a>.
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
<li>
|
<li>
|
||||||
<b><samp>dual_final_frames=1</samp></b><br>
|
<b>dual_final_frames=1</b><br>
|
||||||
Enable workaround for Safari/WebKit bug when using option <samp>--drop-same-frames</samp>.<br>
|
Enable workaround for the Safari/WebKit bug when using option <i>--drop-same-frames</i>.<br>
|
||||||
Without this option, when the frame series is completed, WebKit-based browsers<br>
|
Without this option, when the frame series is completed, WebKit-based browsers<br>
|
||||||
renders the last frame with a delay.
|
renders the last frame with a delay.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<br>
|
<br>
|
||||||
|
<li>
|
||||||
|
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>
|
||||||
|
<br>
|
||||||
|
<li><a href="/?action=stream">/?action=stream</a> as alias to the <a href="/stream">/stream</a>.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const char HTML_INDEX_PAGE[] = " \
|
|||||||
<head> \
|
<head> \
|
||||||
<meta charset=\"utf-8\" /> \
|
<meta charset=\"utf-8\" /> \
|
||||||
<title>uStreamer</title> \
|
<title>uStreamer</title> \
|
||||||
|
<style>body {font-family: monospace;}</style> \
|
||||||
</head> \
|
</head> \
|
||||||
\
|
\
|
||||||
<body> \
|
<body> \
|
||||||
@@ -39,46 +40,56 @@ const char HTML_INDEX_PAGE[] = " \
|
|||||||
<hr> \
|
<hr> \
|
||||||
<ul> \
|
<ul> \
|
||||||
<li> \
|
<li> \
|
||||||
<a href=\"/state\"><b><samp>/state</samp></b></a><br> \
|
<a href=\"/state\"><b>/state</b></a><br> \
|
||||||
Get JSON structure with state of the server. \
|
Get JSON structure with the state of the server. \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
<li> \
|
<li> \
|
||||||
<a href=\"/snapshot\"><b><samp>/snapshot</samp></b></a><br> \
|
<a href=\"/snapshot\"><b>/snapshot</b></a><br> \
|
||||||
Get a current actual image from the server. \
|
Get a current actual image from the server. \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
<li> \
|
<li> \
|
||||||
<a href=\"/stream\"><b><samp>/stream</samp></b></a><br> \
|
<a href=\"/stream\"><b>/stream</b></a><br> \
|
||||||
Get a live stream. Query params:<br> \
|
Get a live stream. Query params:<br> \
|
||||||
<br> \
|
<br> \
|
||||||
<ul> \
|
<ul> \
|
||||||
<li> \
|
<li> \
|
||||||
<b><samp>key=abc123</samp></b><br> \
|
<b>key=abc123</b><br> \
|
||||||
User-defined key, which is part of cookie <samp>stream_client</samp>, which allows<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\"><samp>/state</samp></a>. \
|
the stream client to determine its identifier and view statistics using <a href=\"/state\">/state</a>. \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
<li> \
|
<li> \
|
||||||
<b><samp>extra_headers=1</samp></b><br> \
|
<b>extra_headers=1</b><br> \
|
||||||
Add <samp>X-UStreamer-*</samp> headers to /stream handle (like on <a href=\"/snapshot\"><samp>/snapshot</samp></a>). \
|
Add <i>X-UStreamer-*</i> headers to the <a href=\"/stream\">/stream</a> handle \
|
||||||
|
(like with the <a href=\"/snapshot\">/snapshot</a>). \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
<li> \
|
<li> \
|
||||||
<b><samp>advance_headers=1</samp></b><br> \
|
<b>advance_headers=1</b><br> \
|
||||||
Enable workaround for Chromium/Blink \
|
Enable workaround for the Chromium/Blink bug \
|
||||||
<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\">#527446</a>. \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
<li> \
|
<li> \
|
||||||
<b><samp>dual_final_frames=1</samp></b><br> \
|
<b>dual_final_frames=1</b><br> \
|
||||||
Enable workaround for Safari/WebKit bug when using option <samp>--drop-same-frames</samp>.<br> \
|
Enable workaround for the Safari/WebKit bug when using option <i>--drop-same-frames</i>.<br> \
|
||||||
Without this option, when the frame series is completed, WebKit-based browsers<br> \
|
Without this option, when the frame series is completed, WebKit-based browsers<br> \
|
||||||
renders the last frame with a delay. \
|
renders the last frame with a delay. \
|
||||||
</li> \
|
</li> \
|
||||||
</ul> \
|
</ul> \
|
||||||
</li> \
|
</li> \
|
||||||
<br> \
|
<br> \
|
||||||
|
<li> \
|
||||||
|
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> \
|
||||||
|
<br> \
|
||||||
|
<li><a href=\"/?action=stream\">/?action=stream</a> as alias to the <a href=\"/stream\">/stream</a>.</li> \
|
||||||
|
</ul> \
|
||||||
|
</li> \
|
||||||
</ul> \
|
</ul> \
|
||||||
<br> \
|
<br> \
|
||||||
<hr> \
|
<hr> \
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ INLINE void process_set_name_prefix(int argc, char *argv[], const char *prefix)
|
|||||||
size_t arg_len = strlen(argv[index]);
|
size_t arg_len = strlen(argv[index]);
|
||||||
if (used + arg_len + 16 >= allocated) {
|
if (used + arg_len + 16 >= allocated) {
|
||||||
allocated += arg_len + 2048;
|
allocated += arg_len + 2048;
|
||||||
A_REALLOC(cmdline, allocated);
|
A_REALLOC(cmdline, allocated); // cppcheck-suppress memleakOnRealloc // False-positive (ok with assert)
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat(cmdline, " ");
|
strcat(cmdline, " ");
|
||||||
|
|||||||
21
src/stream.c
21
src/stream.c
@@ -88,7 +88,7 @@ struct _workers_pool_t {
|
|||||||
|
|
||||||
|
|
||||||
static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream);
|
static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream);
|
||||||
static struct _workers_pool_t *_stream_init(struct stream_t *stream);
|
static struct _workers_pool_t *_stream_init_one(struct stream_t *stream);
|
||||||
static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index, unsigned captured_fps);
|
static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index, unsigned captured_fps);
|
||||||
|
|
||||||
static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream);
|
static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream);
|
||||||
@@ -303,13 +303,26 @@ void stream_switch_slowdown(struct stream_t *stream, bool slowdown) {
|
|||||||
|
|
||||||
static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream) {
|
static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream) {
|
||||||
struct _workers_pool_t *pool = NULL;
|
struct _workers_pool_t *pool = NULL;
|
||||||
|
int access_error = 0;
|
||||||
|
|
||||||
LOG_DEBUG("%s: stream->proc->stop=%d", __FUNCTION__, atomic_load(&stream->proc->stop));
|
LOG_DEBUG("%s: stream->proc->stop=%d", __FUNCTION__, atomic_load(&stream->proc->stop));
|
||||||
|
|
||||||
while (!atomic_load(&stream->proc->stop)) {
|
while (!atomic_load(&stream->proc->stop)) {
|
||||||
SEP_INFO('=');
|
if (access(stream->dev->path, R_OK|W_OK) < 0) {
|
||||||
|
if (access_error != errno) {
|
||||||
|
SEP_INFO('=');
|
||||||
|
LOG_PERROR("Can't access device");
|
||||||
|
LOG_INFO("Waiting for the device access ...");
|
||||||
|
access_error = errno;
|
||||||
|
}
|
||||||
|
sleep(stream->dev->error_delay);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
SEP_INFO('=');
|
||||||
|
access_error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ((pool = _stream_init(stream)) == NULL) {
|
if ((pool = _stream_init_one(stream)) == NULL) {
|
||||||
LOG_INFO("Sleeping %u seconds before new stream init ...", stream->dev->error_delay);
|
LOG_INFO("Sleeping %u seconds before new stream init ...", stream->dev->error_delay);
|
||||||
sleep(stream->dev->error_delay);
|
sleep(stream->dev->error_delay);
|
||||||
} else {
|
} else {
|
||||||
@@ -319,7 +332,7 @@ static struct _workers_pool_t *_stream_init_loop(struct stream_t *stream) {
|
|||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct _workers_pool_t *_stream_init(struct stream_t *stream) {
|
static struct _workers_pool_t *_stream_init_one(struct stream_t *stream) {
|
||||||
if (device_open(stream->dev) < 0) {
|
if (device_open(stream->dev) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user