mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 20:26:31 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33a101b4f7 | ||
|
|
9e9039c4e6 | ||
|
|
b3ceec51de | ||
|
|
e3ac4ba6f5 | ||
|
|
fa6b8b44c1 | ||
|
|
8cb7574af2 |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 0.69
|
current_version = 0.71
|
||||||
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}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
| Multithreaded JPEG encoding |  Yes |  No |
|
| Multithreaded JPEG encoding |  Yes |  No |
|
||||||
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi |  Yes |  No |
|
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi |  Yes |  No |
|
||||||
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast <sup>1</sup> |
|
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast <sup>1</sup> |
|
||||||
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal |  Yes |  Partially yes <sup>1</sup> |
|
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal |  Yes |  Partially yes <sup>2</sup> |
|
||||||
| Option to skip frames when streaming<br>static images by HTTP to save traffic |  Yes <sup>2</sup> |  No |
|
| Option to skip frames when streaming<br>static images by HTTP to save traffic |  Yes <sup>2</sup> |  No |
|
||||||
| Streaming via UNIX domain socket |  Yes |  No |
|
| Streaming via UNIX domain socket |  Yes |  No |
|
||||||
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters |  Yes |  No |
|
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters |  Yes |  No |
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
| Многопоточное кодирование JPEG |  Есть |  Нет |
|
| Многопоточное кодирование JPEG |  Есть |  Нет |
|
||||||
| Аппаратное кодирование с помощью [OpenMAX IL](https://www.khronos.org/openmaxil) на Raspberry Pi |  Есть |  Нет |
|
| Аппаратное кодирование с помощью [OpenMAX IL](https://www.khronos.org/openmaxil) на Raspberry Pi |  Есть |  Нет |
|
||||||
| Поведение при физическом отключении<br>устройства от сервера во время работы |  Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова |  Прерывает трансляцию <sup>1</sup> |
|
| Поведение при физическом отключении<br>устройства от сервера во время работы |  Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова |  Прерывает трансляцию <sup>1</sup> |
|
||||||
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности<br>изменения параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) |  Есть |  Условно есть <sup>1</sup> |
|
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности<br>изменения параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) |  Есть |  Условно есть <sup>2</sup> |
|
||||||
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика |  Есть <sup>2</sup> |  Нет |
|
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика |  Есть <sup>2</sup> |  Нет |
|
||||||
| Стрим через UNIX domain socket |  Есть |  Нет |
|
| Стрим через UNIX domain socket |  Есть |  Нет |
|
||||||
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP |  Есть |  Нет |
|
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP |  Есть |  Нет |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=0.69
|
pkgver=0.71
|
||||||
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"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=0.69
|
PKG_VERSION:=0.71
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION "0.69"
|
#define VERSION "0.71"
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ int device_open(struct device_t *dev) {
|
|||||||
_device_open_alloc_picbufs(dev);
|
_device_open_alloc_picbufs(dev);
|
||||||
_device_apply_controls(dev);
|
_device_apply_controls(dev);
|
||||||
|
|
||||||
|
dev->run->n_workers = min_u(dev->run->n_buffers, dev->n_workers);
|
||||||
|
|
||||||
LOG_DEBUG("Device fd=%d initialized", dev->run->fd);
|
LOG_DEBUG("Device fd=%d initialized", dev->run->fd);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -165,6 +167,8 @@ int device_open(struct device_t *dev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void device_close(struct device_t *dev) {
|
void device_close(struct device_t *dev) {
|
||||||
|
dev->run->n_workers = 0;
|
||||||
|
|
||||||
if (dev->run->pictures) {
|
if (dev->run->pictures) {
|
||||||
LOG_DEBUG("Releasing picture buffers ...");
|
LOG_DEBUG("Releasing picture buffers ...");
|
||||||
for (unsigned index = 0; index < dev->run->n_buffers && dev->run->pictures[index].data; ++index) {
|
for (unsigned index = 0; index < dev->run->n_buffers && dev->run->pictures[index].data; ++index) {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ struct device_runtime_t {
|
|||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned format;
|
unsigned format;
|
||||||
unsigned n_buffers;
|
unsigned n_buffers;
|
||||||
// unsigned n_workers; // FIXME
|
unsigned n_workers;
|
||||||
struct hw_buffer_t *hw_buffers;
|
struct hw_buffer_t *hw_buffers;
|
||||||
struct picture_t *pictures;
|
struct picture_t *pictures;
|
||||||
size_t max_raw_image_size;
|
size_t max_raw_image_size;
|
||||||
|
|||||||
137
src/encoder.c
137
src/encoder.c
@@ -20,6 +20,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@@ -66,48 +67,6 @@ struct encoder_t *encoder_init() {
|
|||||||
return encoder;
|
return encoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
assert(encoder->type != ENCODER_TYPE_UNKNOWN);
|
|
||||||
// XXX: Тут нет гонки, потому что encoder_prepare() запускается еще до существования других потоков
|
|
||||||
encoder->run->type = encoder->type;
|
|
||||||
encoder->run->quality = encoder->quality;
|
|
||||||
|
|
||||||
LOG_INFO("Using JPEG quality: %u%%", encoder->quality);
|
|
||||||
|
|
||||||
# ifdef WITH_OMX_ENCODER
|
|
||||||
if (encoder->run->type == ENCODER_TYPE_OMX) {
|
|
||||||
LOG_DEBUG("Preparing OMX JPEG encoder ...");
|
|
||||||
|
|
||||||
if (dev->n_workers > OMX_MAX_ENCODERS) {
|
|
||||||
LOG_INFO("OMX JPEG encoder sets limit for worker threads: %u", OMX_MAX_ENCODERS);
|
|
||||||
dev->n_workers = OMX_MAX_ENCODERS;
|
|
||||||
}
|
|
||||||
encoder->run->n_omxs = dev->n_workers;
|
|
||||||
|
|
||||||
A_CALLOC(encoder->run->omxs, encoder->run->n_omxs);
|
|
||||||
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
|
|
||||||
if ((encoder->run->omxs[index] = omx_encoder_init()) == NULL) {
|
|
||||||
goto use_fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
# pragma GCC diagnostic ignored "-Wunused-label"
|
|
||||||
# pragma GCC diagnostic push
|
|
||||||
use_fallback:
|
|
||||||
LOG_ERROR("Can't initialize selected encoder, using CPU instead it");
|
|
||||||
encoder->run->type = ENCODER_TYPE_CPU;
|
|
||||||
encoder->run->quality = encoder->quality;
|
|
||||||
# pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
void encoder_destroy(struct encoder_t *encoder) {
|
void encoder_destroy(struct encoder_t *encoder) {
|
||||||
# ifdef WITH_OMX_ENCODER
|
# ifdef WITH_OMX_ENCODER
|
||||||
if (encoder->run->omxs) {
|
if (encoder->run->omxs) {
|
||||||
@@ -142,52 +101,87 @@ const char *encoder_type_to_string(enum encoder_type_t type) {
|
|||||||
return _ENCODER_TYPES[0].name;
|
return _ENCODER_TYPES[0].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
|
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
|
||||||
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
|
enum encoder_type_t type = (encoder->run->cpu_forced ? ENCODER_TYPE_CPU : encoder->type);
|
||||||
|
unsigned quality = encoder->quality;
|
||||||
|
bool cpu_forced = false;
|
||||||
|
|
||||||
if (
|
if ((dev->run->format == V4L2_PIX_FMT_MJPEG || dev->run->format == V4L2_PIX_FMT_JPEG) && type != ENCODER_TYPE_HW) {
|
||||||
(dev->run->format == V4L2_PIX_FMT_MJPEG || dev->run->format == V4L2_PIX_FMT_JPEG)
|
|
||||||
&& encoder->run->type != ENCODER_TYPE_HW
|
|
||||||
) {
|
|
||||||
LOG_INFO("Switching to HW JPEG encoder because the input format is (M)JPEG");
|
LOG_INFO("Switching to HW JPEG encoder because the input format is (M)JPEG");
|
||||||
A_MUTEX_LOCK(&encoder->run->mutex);
|
type = ENCODER_TYPE_HW;
|
||||||
encoder->run->type = ENCODER_TYPE_HW;
|
|
||||||
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encoder->run->type == ENCODER_TYPE_HW) {
|
if (type == ENCODER_TYPE_HW) {
|
||||||
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
|
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
|
||||||
LOG_INFO("Switching to CPU JPEG encoder because the input format is not (M)JPEG");
|
LOG_INFO("Switching to CPU JPEG encoder because the input format is not (M)JPEG");
|
||||||
goto use_fallback;
|
goto use_cpu;
|
||||||
}
|
}
|
||||||
if (hw_encoder_prepare_live(dev, encoder->quality) < 0) {
|
|
||||||
A_MUTEX_LOCK(&encoder->run->mutex);
|
if (hw_encoder_prepare(dev, quality) < 0) {
|
||||||
encoder->run->quality = 0;
|
quality = 0;
|
||||||
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
|
||||||
LOG_INFO("Using JPEG quality: HW-default");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->run->n_workers = 1;
|
||||||
}
|
}
|
||||||
# ifdef WITH_OMX_ENCODER
|
# ifdef WITH_OMX_ENCODER
|
||||||
else if (encoder->run->type == ENCODER_TYPE_OMX) {
|
else if (type == ENCODER_TYPE_OMX) {
|
||||||
|
LOG_DEBUG("Preparing OMX JPEG encoder ...");
|
||||||
|
|
||||||
|
if (dev->run->n_workers > OMX_MAX_ENCODERS) {
|
||||||
|
LOG_INFO("OMX JPEG encoder sets limit for worker threads: %u", OMX_MAX_ENCODERS);
|
||||||
|
dev->run->n_workers = OMX_MAX_ENCODERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoder->run->omxs == NULL) {
|
||||||
|
A_CALLOC(encoder->run->omxs, OMX_MAX_ENCODERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
|
||||||
|
for (; encoder->run->n_omxs < dev->run->n_workers; ++encoder->run->n_omxs) {
|
||||||
|
if ((encoder->run->omxs[encoder->run->n_omxs] = omx_encoder_init()) == NULL) {
|
||||||
|
LOG_ERROR("Can't initialize OMX JPEG encoder, falling back to CPU");
|
||||||
|
goto force_cpu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
|
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
|
||||||
if (omx_encoder_prepare_live(encoder->run->omxs[index], dev, encoder->quality) < 0) {
|
if (omx_encoder_prepare(encoder->run->omxs[index], dev, quality) < 0) {
|
||||||
LOG_ERROR("Can't prepare OMX JPEG encoder, falling back to CPU");
|
LOG_ERROR("Can't prepare OMX JPEG encoder, falling back to CPU");
|
||||||
goto use_fallback;
|
goto force_cpu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
return;
|
goto ok;
|
||||||
|
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-label"
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
force_cpu:
|
||||||
|
cpu_forced = true;
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
use_cpu:
|
||||||
|
type = ENCODER_TYPE_CPU;
|
||||||
|
quality = encoder->quality;
|
||||||
|
|
||||||
|
ok:
|
||||||
|
if (quality == 0) {
|
||||||
|
LOG_INFO("Using JPEG quality: encoder default");
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Using JPEG quality: %u%%", quality);
|
||||||
|
}
|
||||||
|
|
||||||
use_fallback:
|
|
||||||
A_MUTEX_LOCK(&encoder->run->mutex);
|
A_MUTEX_LOCK(&encoder->run->mutex);
|
||||||
encoder->run->type = ENCODER_TYPE_CPU;
|
encoder->run->type = type;
|
||||||
encoder->run->quality = encoder->quality;
|
encoder->run->quality = quality;
|
||||||
|
if (cpu_forced) {
|
||||||
|
encoder->run->cpu_forced = true;
|
||||||
|
}
|
||||||
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-label"
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index) {
|
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index) {
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
@@ -195,14 +189,14 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
|
|||||||
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
|
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
|
||||||
|
|
||||||
if (encoder->run->type == ENCODER_TYPE_CPU) {
|
if (encoder->run->type == ENCODER_TYPE_CPU) {
|
||||||
cpu_encoder_compress_buffer(dev, buf_index, encoder->quality);
|
cpu_encoder_compress_buffer(dev, buf_index, encoder->run->quality);
|
||||||
} else if (encoder->run->type == ENCODER_TYPE_HW) {
|
} else if (encoder->run->type == ENCODER_TYPE_HW) {
|
||||||
hw_encoder_compress_buffer(dev, buf_index);
|
hw_encoder_compress_buffer(dev, buf_index);
|
||||||
}
|
}
|
||||||
# ifdef WITH_OMX_ENCODER
|
# ifdef WITH_OMX_ENCODER
|
||||||
else if (encoder->run->type == ENCODER_TYPE_OMX) {
|
else if (encoder->run->type == ENCODER_TYPE_OMX) {
|
||||||
if (omx_encoder_compress_buffer(encoder->run->omxs[worker_number], dev, buf_index) < 0) {
|
if (omx_encoder_compress_buffer(encoder->run->omxs[worker_number], dev, buf_index) < 0) {
|
||||||
goto use_fallback;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
@@ -211,11 +205,10 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
|
|||||||
|
|
||||||
# pragma GCC diagnostic ignored "-Wunused-label"
|
# pragma GCC diagnostic ignored "-Wunused-label"
|
||||||
# pragma GCC diagnostic push
|
# pragma GCC diagnostic push
|
||||||
use_fallback:
|
error:
|
||||||
LOG_INFO("Error while compressing, falling back to CPU");
|
LOG_INFO("Error while compressing buffer, falling back to CPU");
|
||||||
A_MUTEX_LOCK(&encoder->run->mutex);
|
A_MUTEX_LOCK(&encoder->run->mutex);
|
||||||
encoder->run->type = ENCODER_TYPE_CPU;
|
encoder->run->cpu_forced = true;
|
||||||
encoder->run->quality = encoder->quality;
|
|
||||||
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
A_MUTEX_UNLOCK(&encoder->run->mutex);
|
||||||
return -1;
|
return -1;
|
||||||
# pragma GCC diagnostic pop
|
# pragma GCC diagnostic pop
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "pthread.h"
|
#include "pthread.h"
|
||||||
|
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
@@ -51,6 +53,7 @@ enum encoder_type_t {
|
|||||||
struct encoder_runtime_t {
|
struct encoder_runtime_t {
|
||||||
enum encoder_type_t type;
|
enum encoder_type_t type;
|
||||||
unsigned quality;
|
unsigned quality;
|
||||||
|
bool cpu_forced;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
# ifdef WITH_OMX_ENCODER
|
# ifdef WITH_OMX_ENCODER
|
||||||
@@ -74,6 +77,4 @@ enum encoder_type_t encoder_parse_type(const char *str);
|
|||||||
const char *encoder_type_to_string(enum encoder_type_t type);
|
const char *encoder_type_to_string(enum encoder_type_t type);
|
||||||
|
|
||||||
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev);
|
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev);
|
||||||
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev);
|
|
||||||
|
|
||||||
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index);
|
int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, unsigned worker_number, unsigned buf_index);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ static bool _is_huffman(const unsigned char *data);
|
|||||||
static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size);
|
static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size);
|
||||||
|
|
||||||
|
|
||||||
int hw_encoder_prepare_live(struct device_t *dev, unsigned quality) {
|
int hw_encoder_prepare(struct device_t *dev, unsigned quality) {
|
||||||
struct v4l2_jpegcompression comp;
|
struct v4l2_jpegcompression comp;
|
||||||
|
|
||||||
MEMSET_ZERO(comp);
|
MEMSET_ZERO(comp);
|
||||||
|
|||||||
@@ -25,5 +25,5 @@
|
|||||||
#include "../../device.h"
|
#include "../../device.h"
|
||||||
|
|
||||||
|
|
||||||
int hw_encoder_prepare_live(struct device_t *dev, unsigned quality);
|
int hw_encoder_prepare(struct device_t *dev, unsigned quality);
|
||||||
void hw_encoder_compress_buffer(struct device_t *dev, unsigned index);
|
void hw_encoder_compress_buffer(struct device_t *dev, unsigned index);
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
|
|||||||
free(omx);
|
free(omx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality) {
|
int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality) {
|
||||||
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
|
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,5 +53,5 @@ struct omx_encoder_t {
|
|||||||
struct omx_encoder_t *omx_encoder_init();
|
struct omx_encoder_t *omx_encoder_init();
|
||||||
void omx_encoder_destroy(struct omx_encoder_t *omx);
|
void omx_encoder_destroy(struct omx_encoder_t *omx);
|
||||||
|
|
||||||
int omx_encoder_prepare_live(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality);
|
int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev, unsigned quality);
|
||||||
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index);
|
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index);
|
||||||
|
|||||||
@@ -266,7 +266,6 @@ static bool _http_get_param_true(struct evkeyvalq *params, const char *key) {
|
|||||||
if ((value_str = evhttp_find_header(params, key)) != NULL) {
|
if ((value_str = evhttp_find_header(params, key)) != NULL) {
|
||||||
if (
|
if (
|
||||||
value_str[0] == '1'
|
value_str[0] == '1'
|
||||||
|| value_str[0] == 'y'
|
|
||||||
|| !evutil_ascii_strcasecmp(value_str, "true")
|
|| !evutil_ascii_strcasecmp(value_str, "true")
|
||||||
|| !evutil_ascii_strcasecmp(value_str, "yes")
|
|| !evutil_ascii_strcasecmp(value_str, "yes")
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
|
|||||||
printf(" -b|--buffers <N> ─────────────── The number of buffers to receive data from the device.\n");
|
printf(" -b|--buffers <N> ─────────────── The number of buffers to receive data from the device.\n");
|
||||||
printf(" Each buffer may processed using an intermediate thread.\n");
|
printf(" Each buffer may processed using an intermediate thread.\n");
|
||||||
printf(" Default: %u (the number of CPU cores (but not more 4) + 1).\n\n", dev->n_buffers);
|
printf(" Default: %u (the number of CPU cores (but not more 4) + 1).\n\n", dev->n_buffers);
|
||||||
printf(" -w|--workers <N> ─────────────── The number of worker threads. Default: %u (== --buffers).\n\n", dev->n_workers);
|
printf(" -w|--workers <N> ─────────────── The number of worker threads but not more than buffers.\n");
|
||||||
|
printf(" Default: %u (== --buffers).\n\n", dev->n_workers);
|
||||||
printf(" -q|--quality <N> ─────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality);
|
printf(" -q|--quality <N> ─────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.\n\n", encoder->quality);
|
||||||
printf(" -c|--encoder <type> ──────────── Use specified encoder. It may affects to workers number.\n");
|
printf(" -c|--encoder <type> ──────────── Use specified encoder. It may affects to workers number.\n");
|
||||||
printf(" Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
|
printf(" Available: %s; default: CPU.\n\n", ENCODER_TYPES_STR);
|
||||||
@@ -383,7 +384,6 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if ((exit_code = _parse_options(argc, argv, dev, encoder, server)) == 0) {
|
if ((exit_code = _parse_options(argc, argv, dev, encoder, server)) == 0) {
|
||||||
_install_signal_handlers();
|
_install_signal_handlers();
|
||||||
encoder_prepare(encoder, dev);
|
|
||||||
|
|
||||||
pthread_t stream_loop_tid;
|
pthread_t stream_loop_tid;
|
||||||
pthread_t server_loop_tid;
|
pthread_t server_loop_tid;
|
||||||
|
|||||||
18
src/stream.c
18
src/stream.c
@@ -119,7 +119,7 @@ void stream_loop(struct stream_t *stream) {
|
|||||||
|
|
||||||
LOG_PERF("##### Raw frame accepted; worker = %u", free_worker_number);
|
LOG_PERF("##### Raw frame accepted; worker = %u", free_worker_number);
|
||||||
} else {
|
} else {
|
||||||
for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
|
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
||||||
if (
|
if (
|
||||||
!atomic_load(&pool.workers[number].has_job) && (
|
!atomic_load(&pool.workers[number].has_job) && (
|
||||||
free_worker_number == -1
|
free_worker_number == -1
|
||||||
@@ -336,7 +336,7 @@ static long double _stream_get_fluency_delay(struct device_t *dev, struct worker
|
|||||||
long double min_delay;
|
long double min_delay;
|
||||||
long double soft_delay;
|
long double soft_delay;
|
||||||
|
|
||||||
for (unsigned number = 0; number < dev->n_workers; ++number) {
|
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
|
||||||
# define WORKER(_next) pool->workers[number]._next
|
# define WORKER(_next) pool->workers[number]._next
|
||||||
|
|
||||||
A_MUTEX_LOCK(&WORKER(last_comp_time_mutex));
|
A_MUTEX_LOCK(&WORKER(last_comp_time_mutex));
|
||||||
@@ -347,9 +347,9 @@ static long double _stream_get_fluency_delay(struct device_t *dev, struct worker
|
|||||||
|
|
||||||
# undef WORKER
|
# undef WORKER
|
||||||
}
|
}
|
||||||
avg_comp_time = sum_comp_time / dev->n_workers; // Среднее время работы воркеров
|
avg_comp_time = sum_comp_time / dev->run->n_workers; // Среднее время работы воркеров
|
||||||
|
|
||||||
min_delay = avg_comp_time / dev->n_workers; // Среднее время работы размазывается на N воркеров
|
min_delay = avg_comp_time / dev->run->n_workers; // Среднее время работы размазывается на N воркеров
|
||||||
|
|
||||||
if (dev->desired_fps > 0 && min_delay > 0) {
|
if (dev->desired_fps > 0 && min_delay > 0) {
|
||||||
// Искусственное время задержки на основе желаемого FPS, если включен --desired-fps
|
// Искусственное время задержки на основе желаемого FPS, если включен --desired-fps
|
||||||
@@ -389,7 +389,7 @@ static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder_prepare_live(pool->encoder, stream->dev);
|
encoder_prepare(pool->encoder, stream->dev);
|
||||||
|
|
||||||
_stream_init_workers(stream, pool);
|
_stream_init_workers(stream, pool);
|
||||||
|
|
||||||
@@ -401,15 +401,15 @@ static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t *pool) {
|
static void _stream_init_workers(struct stream_t *stream, struct workers_pool_t *pool) {
|
||||||
LOG_INFO("Spawning %u workers ...", stream->dev->n_workers);
|
LOG_INFO("Spawning %u workers ...", stream->dev->run->n_workers);
|
||||||
|
|
||||||
atomic_store(&pool->workers_stop, false);
|
atomic_store(&pool->workers_stop, false);
|
||||||
A_CALLOC(pool->workers, stream->dev->n_workers);
|
A_CALLOC(pool->workers, stream->dev->run->n_workers);
|
||||||
|
|
||||||
A_MUTEX_INIT(&pool->free_workers_mutex);
|
A_MUTEX_INIT(&pool->free_workers_mutex);
|
||||||
A_COND_INIT(&pool->free_workers_cond);
|
A_COND_INIT(&pool->free_workers_cond);
|
||||||
|
|
||||||
for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
|
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
||||||
# define WORKER(_next) pool->workers[number]._next
|
# define WORKER(_next) pool->workers[number]._next
|
||||||
|
|
||||||
pool->free_workers += 1;
|
pool->free_workers += 1;
|
||||||
@@ -492,7 +492,7 @@ static void _stream_destroy_workers(struct stream_t *stream, struct workers_pool
|
|||||||
LOG_INFO("Destroying workers ...");
|
LOG_INFO("Destroying workers ...");
|
||||||
|
|
||||||
atomic_store(&pool->workers_stop, true);
|
atomic_store(&pool->workers_stop, true);
|
||||||
for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
|
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
||||||
# define WORKER(_next) pool->workers[number]._next
|
# define WORKER(_next) pool->workers[number]._next
|
||||||
|
|
||||||
A_MUTEX_LOCK(&WORKER(has_job_mutex));
|
A_MUTEX_LOCK(&WORKER(has_job_mutex));
|
||||||
|
|||||||
Reference in New Issue
Block a user