mjpeg v4l2 encoding

This commit is contained in:
Maxim Devaev 2021-11-23 19:26:04 +03:00
parent 235a1765d2
commit 222b9a0309
20 changed files with 51 additions and 1112 deletions

View File

@ -1,4 +1,3 @@
#define CHAR_BIT 8
#define WITH_OMX
#define WITH_GPIO
#define JANUS_PLUGIN_INIT(...) { __VA_ARGS__ }

View File

@ -23,9 +23,9 @@ For example, the recommended way of running µStreamer with Auvidea B101 on a Ra
.RS
\fB\-\-format=uyvy \e\fR # Device input format
.nf
\fB\-\-encoder=omx \e\fR # Hardware encoding with OpenMAX
\fB\-\-encoder=v4l2 \e\fR # Hardware encoding with V4L2 M2M intraface
.nf
\fB\-\-workers=3 \e\fR # Maximum workers for OpenMAX
\fB\-\-workers=3 \e\fR # Maximum workers for V4L2 encoder
.nf
\fB\-\-persistent \e\fR # Don\'t re\-initialize device on timeout (for example when HDMI cable was disconnected)
.nf
@ -91,14 +91,14 @@ Use specified encoder. It may affect the number of workers.
CPU ─ Software MJPG encoding (default).
OMX ─ GPU hardware accelerated MJPG encoding with OpenMax (required \fBWITH_OMX\fR feature).
HW ─ Use pre-encoded MJPG frames directly from camera hardware.
V4L2 ─ GPU-accelerated MJPG encoding.
NOOP ─ Don't compress MJPG stream (do nothing).
.TP
.BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,...
It doesn't do anything. Still here for compatibility. Required \fBWITH_OMX\fR feature.
It doesn't do anything. Still here for compatibility.
.TP
.BR \-k\ \fIpath ", " \-\-blank\ \fIpath
Path to JPEG file that will be shown when the device is disconnected during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.

View File

@ -21,11 +21,6 @@ if [ -e /usr/bin/python3 ]; then
depends+=(python)
makedepends+=(python-setuptools)
fi
if [ -e /opt/vc/include/IL/OMX_Core.h ]; then
depends+=(raspberrypi-firmware)
makedepends+=(raspberrypi-firmware)
_options="$_options WITH_OMX=1"
fi
if [ -e /usr/include/janus/plugins/plugin.h ];then
depends+=(janus-gateway-pikvm)
makedepends+=(janus-gateway-pikvm)

View File

@ -7,13 +7,12 @@ RUN apt-get update \
gcc \
libjpeg8-dev \
libbsd-dev \
libraspberrypi-dev \
libgpiod-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build/ustreamer/
COPY . .
RUN make -j5 WITH_OMX=1 WITH_GPIO=1
RUN make -j5 WITH_GPIO=1
RUN ["cross-build-end"]
FROM balenalib/raspberrypi3-debian:run as RUN

View File

@ -5,13 +5,12 @@ RUN apt-get update \
gcc \
libjpeg8-dev \
libbsd-dev \
libraspberrypi-dev \
libgpiod-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build/ustreamer/
COPY . .
RUN make -j5 WITH_OMX=1 WITH_GPIO=1
RUN make -j5 WITH_GPIO=1
FROM balenalib/raspberrypi3-debian:run as RUN

View File

@ -5,9 +5,6 @@ CC ?= gcc
CFLAGS ?= -O3
LDFLAGS ?=
RPI_VC_HEADERS ?= /opt/vc/include
RPI_VC_LIBS ?= /opt/vc/lib
# =====
_USTR = ustreamer.bin
@ -26,6 +23,7 @@ _USTR_SRCS = $(shell ls \
ustreamer/data/*.c \
ustreamer/encoders/cpu/*.c \
ustreamer/encoders/hw/*.c \
ustreamer/encoders/v4l2/*.c \
ustreamer/h264/*.c \
)
@ -43,15 +41,6 @@ $(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
endef
ifneq ($(call optbool,$(WITH_OMX)),)
_USTR_LIBS += -lbcm_host -lvcos -lvcsm -lopenmaxil -L$(RPI_VC_LIBS)
override _CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
_USTR_SRCS += $(shell ls \
ustreamer/encoders/omx/*.c \
)
endif
ifneq ($(call optbool,$(WITH_GPIO)),)
_USTR_LIBS += -lgpiod
override _CFLAGS += -DWITH_GPIO

View File

@ -29,9 +29,7 @@ static const struct {
} _ENCODER_TYPES[] = {
{"CPU", ENCODER_TYPE_CPU},
{"HW", ENCODER_TYPE_HW},
# ifdef WITH_OMX
{"OMX", ENCODER_TYPE_OMX},
# endif
{"V4L2", ENCODER_TYPE_V4L2},
{"NOOP", ENCODER_TYPE_NOOP},
};
@ -60,22 +58,23 @@ encoder_s *encoder_init(void) {
}
void encoder_destroy(encoder_s *enc) {
# ifdef WITH_OMX
if (ER(omxs)) {
for (unsigned index = 0; index < ER(n_omxs); ++index) {
if (ER(omxs[index])) {
omx_encoder_destroy(ER(omxs[index]));
if (ER(m2ms)) {
for (unsigned index = 0; index < ER(n_m2ms); ++index) {
if (ER(m2ms[index])) {
m2m_encoder_destroy(ER(m2ms[index]));
}
}
free(ER(omxs));
free(ER(m2ms));
}
# endif
A_MUTEX_DESTROY(&ER(mutex));
free(enc->run);
free(enc);
}
encoder_type_e encoder_parse_type(const char *str) {
if (!strcasecmp(str, "OMX")) {
return ENCODER_TYPE_V4L2; // Just for compatibility
}
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
return _ENCODER_TYPES[index].type;
@ -113,62 +112,27 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
}
quality = DR(jpeg_quality);
n_workers = 1;
}
# ifdef WITH_OMX
else if (type == ENCODER_TYPE_OMX) {
if (align_size(DR(width), 32) != DR(width)) {
LOG_INFO("Switching to CPU encoder: OMX can't handle width=%u ...", DR(width));
goto use_cpu;
} else if (type == ENCODER_TYPE_V4L2) {
LOG_DEBUG("Preparing V4L2 encoder ...");
if (ER(m2ms) == NULL) {
A_CALLOC(ER(m2ms), n_workers);
}
LOG_DEBUG("Preparing OMX encoder ...");
if (n_workers > OMX_MAX_ENCODERS) {
LOG_INFO("OMX encoder sets limit for worker threads: %u", OMX_MAX_ENCODERS);
n_workers = OMX_MAX_ENCODERS;
}
if (ER(omxs) == NULL) {
A_CALLOC(ER(omxs), OMX_MAX_ENCODERS);
}
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
for (; ER(n_omxs) < n_workers; ++ER(n_omxs)) {
if ((ER(omxs[ER(n_omxs)]) = omx_encoder_init()) == NULL) {
LOG_ERROR("Can't initialize OMX encoder, falling back to CPU");
goto force_cpu;
}
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
char name[32];
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
m2m_option_s options[] = {{NULL, false, 0, 0}};
ER(m2ms[ER(n_m2ms)]) = m2m_encoder_init(name, "/dev/video11", V4L2_PIX_FMT_MJPEG, 0, options);
}
frame_s frame = {0};
frame.width = DR(width);
frame.height = DR(height);
frame.format = DR(format);
frame.stride = DR(stride);
for (unsigned index = 0; index < ER(n_omxs); ++index) {
int omx_error = omx_encoder_prepare(ER(omxs[index]), &frame, quality);
if (omx_error == -2) {
goto use_cpu;
} else if (omx_error < 0) {
goto force_cpu;
}
}
}
# endif
else if (type == ENCODER_TYPE_NOOP) {
} else if (type == ENCODER_TYPE_NOOP) {
n_workers = 1;
quality = 0;
}
goto ok;
# ifdef WITH_OMX
force_cpu:
LOG_ERROR("Forced CPU encoder permanently");
cpu_forced = true;
# endif
use_cpu:
type = ENCODER_TYPE_CPU;
quality = dev->jpeg_quality;
@ -248,19 +212,20 @@ static bool _worker_run_job(worker_s *wr) {
if (ER(type) == ENCODER_TYPE_CPU) {
LOG_VERBOSE("Compressing buffer using CPU");
cpu_encoder_compress(src, dest, ER(quality));
} else if (ER(type) == ENCODER_TYPE_HW) {
LOG_VERBOSE("Compressing buffer using HW (just copying)");
hw_encoder_compress(src, dest);
}
# ifdef WITH_OMX
else if (ER(type) == ENCODER_TYPE_OMX) {
LOG_VERBOSE("Compressing buffer using OMX");
if (omx_encoder_compress(ER(omxs[wr->number]), src, dest) < 0) {
goto error;
} else if (ER(type) == ENCODER_TYPE_V4L2) {
LOG_VERBOSE("Compressing buffer using V4L2");
if (!m2m_encoder_ensure_ready(ER(m2ms[wr->number]), src)) {
if (m2m_encoder_compress(ER(m2ms[wr->number]), src, dest, false) < 0) {
goto error;
}
}
}
# endif
else if (ER(type) == ENCODER_TYPE_NOOP) {
} else if (ER(type) == ENCODER_TYPE_NOOP) {
LOG_VERBOSE("Compressing buffer using NOOP (do nothing)");
usleep(5000); // Просто чтобы работала логика desired_fps
}
@ -274,7 +239,6 @@ static bool _worker_run_job(worker_s *wr) {
return true;
# ifdef WITH_OMX
error:
LOG_ERROR("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf.index);
LOG_ERROR("Error while compressing buffer, falling back to CPU");
@ -282,7 +246,6 @@ static bool _worker_run_job(worker_s *wr) {
ER(cpu_forced) = true;
A_MUTEX_UNLOCK(&ER(mutex));
return false;
# endif
# undef ER
}

View File

@ -37,30 +37,19 @@
#include "device.h"
#include "workers.h"
#include "m2m.h"
#include "encoders/cpu/encoder.h"
#include "encoders/hw/encoder.h"
#ifdef WITH_OMX
# include "encoders/omx/encoder.h"
# define ENCODER_TYPES_OMX_HINT ", OMX"
#else
# define ENCODER_TYPES_OMX_HINT ""
#endif
#define ENCODER_TYPES_STR \
"CPU, HW" \
ENCODER_TYPES_OMX_HINT \
", NOOP"
#define ENCODER_TYPES_STR "CPU, HW, V4L2, NOOP"
typedef enum {
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
ENCODER_TYPE_CPU,
ENCODER_TYPE_HW,
# ifdef WITH_OMX
ENCODER_TYPE_OMX,
# endif
ENCODER_TYPE_V4L2,
ENCODER_TYPE_NOOP,
} encoder_type_e;
@ -70,10 +59,8 @@ typedef struct {
bool cpu_forced;
pthread_mutex_t mutex;
# ifdef WITH_OMX
unsigned n_omxs;
omx_encoder_s **omxs;
# endif
unsigned n_m2ms;
m2m_encoder_s **m2ms;
} encoder_runtime_s;
typedef struct {

View File

@ -1,154 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "component.h"
static int _omx_component_wait_port_changed(OMX_HANDLETYPE *comp, OMX_U32 port, OMX_BOOL enabled);
static int _omx_component_wait_state_changed(OMX_HANDLETYPE *comp, OMX_STATETYPE wanted);
int omx_component_enable_port(OMX_HANDLETYPE *comp, OMX_U32 port) {
OMX_ERRORTYPE error;
LOG_DEBUG("Enabling OMX port %u ...", port);
if ((error = OMX_SendCommand(*comp, OMX_CommandPortEnable, port, NULL)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't enable OMX port %u", port);
return -1;
}
return _omx_component_wait_port_changed(comp, port, OMX_TRUE);
}
int omx_component_disable_port(OMX_HANDLETYPE *comp, OMX_U32 port) {
OMX_ERRORTYPE error;
LOG_DEBUG("Disabling OMX port %u ...", port);
if ((error = OMX_SendCommand(*comp, OMX_CommandPortDisable, port, NULL)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't disable OMX port %u", port);
return -1;
}
return _omx_component_wait_port_changed(comp, port, OMX_FALSE);
}
int omx_component_get_portdef(OMX_HANDLETYPE *comp, OMX_PARAM_PORTDEFINITIONTYPE *portdef, OMX_U32 port) {
OMX_ERRORTYPE error;
// cppcheck-suppress redundantPointerOp
OMX_INIT_STRUCTURE(*portdef);
portdef->nPortIndex = port;
LOG_DEBUG("Fetching OMX port %u definition ...", port);
if ((error = OMX_GetParameter(*comp, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't get OMX port %u definition", port);
return -1;
}
return 0;
}
int omx_component_set_portdef(OMX_HANDLETYPE *comp, OMX_PARAM_PORTDEFINITIONTYPE *portdef) {
OMX_ERRORTYPE error;
LOG_DEBUG("Writing OMX port %u definition ...", portdef->nPortIndex);
if ((error = OMX_SetParameter(*comp, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't set OMX port %u definition", portdef->nPortIndex);
return -1;
}
return 0;
}
int omx_component_set_state(OMX_HANDLETYPE *comp, OMX_STATETYPE state) {
const char *state_str = omx_state_to_string(state);
OMX_ERRORTYPE error;
LOG_DEBUG("Switching component state to %s ...", state_str);
int retries = 50;
do {
error = OMX_SendCommand(*comp, OMX_CommandStateSet, state, NULL);
if (error == OMX_ErrorNone) {
return _omx_component_wait_state_changed(comp, state);
} else if (error == OMX_ErrorInsufficientResources && retries) {
// Иногда железо не инициализируется, хз почему, просто ретраим, со второй попытки сработает
if (retries > 45) {
LOG_VERBOSE("Can't switch OMX component state to %s, need to retry: %s",
state_str, omx_error_to_string(error));
} else {
LOG_ERROR_OMX(error, "Can't switch OMX component state to %s, need to retry", state_str);
}
retries -= 1;
usleep(8000);
} else {
break;
}
} while (retries);
LOG_ERROR_OMX(error, "Can't switch OMX component state to %s", state_str);
return -1;
}
static int _omx_component_wait_port_changed(OMX_HANDLETYPE *comp, OMX_U32 port, OMX_BOOL enabled) {
OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef;
OMX_INIT_STRUCTURE(portdef);
portdef.nPortIndex = port;
int retries = 50;
do {
if ((error = OMX_GetParameter(*comp, OMX_IndexParamPortDefinition, &portdef)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't get OMX port %u definition for waiting", port);
return -1;
}
if (portdef.bEnabled != enabled) {
LOG_DEBUG("Waiting for OMX %s port %u", (enabled ? "enabling" : "disabling"), port);
retries -= 1;
usleep(8000);
}
} while (portdef.bEnabled != enabled && retries);
LOG_DEBUG("OMX port %u %s", port, (enabled ? "enabled" : "disabled"));
return (portdef.bEnabled == enabled ? 0 : -1);
}
static int _omx_component_wait_state_changed(OMX_HANDLETYPE *comp, OMX_STATETYPE wanted) {
OMX_ERRORTYPE error;
OMX_STATETYPE state;
int retries = 50;
do {
if ((error = OMX_GetState(*comp, &state)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Failed to get OMX component state");
return -1;
}
if (state != wanted) {
LOG_DEBUG("Waiting when OMX component state changes to %s", omx_state_to_string(wanted));
retries -= 1;
usleep(8000);
}
} while (state != wanted && retries);
LOG_DEBUG("Switched OMX component state to %s", omx_state_to_string(wanted))
return (state == wanted ? 0 : -1);
}

View File

@ -1,53 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include <string.h>
#include <unistd.h>
#include <IL/OMX_Core.h>
#include <IL/OMX_Component.h>
#include "../../../libs/logging.h"
#include "formatters.h"
#define OMX_INIT_STRUCTURE(_var) { \
memset(&(_var), 0, sizeof(_var)); \
(_var).nSize = sizeof(_var); \
(_var).nVersion.nVersion = OMX_VERSION; \
(_var).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \
(_var).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \
(_var).nVersion.s.nRevision = OMX_VERSION_REVISION; \
(_var).nVersion.s.nStep = OMX_VERSION_STEP; \
}
int omx_component_enable_port(OMX_HANDLETYPE *comp, OMX_U32 port);
int omx_component_disable_port(OMX_HANDLETYPE *comp, OMX_U32 port);
int omx_component_get_portdef(OMX_HANDLETYPE *comp, OMX_PARAM_PORTDEFINITIONTYPE *portdef, OMX_U32 port);
int omx_component_set_portdef(OMX_HANDLETYPE *comp, OMX_PARAM_PORTDEFINITIONTYPE *portdef);
int omx_component_set_state(OMX_HANDLETYPE *comp, OMX_STATETYPE state);

View File

@ -1,444 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "encoder.h"
static const OMX_U32 _INPUT_PORT = 340;
static const OMX_U32 _OUTPUT_PORT = 341;
static int _omx_init_component(omx_encoder_s *omx);
static int _omx_init_disable_ports(omx_encoder_s *omx);
static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame);
static int _omx_setup_output(omx_encoder_s *omx, unsigned quality);
static int _omx_encoder_clear_ports(omx_encoder_s *omx);
static OMX_ERRORTYPE _omx_event_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1,
UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data);
static OMX_ERRORTYPE _omx_input_required_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buf);
static OMX_ERRORTYPE _omx_output_available_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buf);
omx_encoder_s *omx_encoder_init(void) {
// Some theory:
// - http://www.fourcc.org/yuv.php
// - https://kwasi-ich.de/blog/2017/11/26/omx/
// - https://github.com/hopkinskong/rpi-omx-jpeg-encode/blob/master/jpeg_bench.cpp
// - https://github.com/kwasmich/OMXPlayground/blob/master/omxJPEGEnc.c
// - https://github.com/gagle/raspberrypi-openmax-jpeg/blob/master/jpeg.c
// - https://www.raspberrypi.org/forums/viewtopic.php?t=154790
// - https://bitbucket.org/bensch128/omxjpegencode/src/master/jpeg_encoder.cpp
// - http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/image_encode.html
LOG_INFO("Initializing OMX encoder ...");
omx_encoder_s *omx;
A_CALLOC(omx, 1);
if (vcos_semaphore_create(&omx->handler_sem, "handler_sem", 0) != VCOS_SUCCESS) {
LOG_ERROR("Can't create VCOS semaphore");
goto error;
}
omx->i_handler_sem = true;
if (_omx_init_component(omx) < 0) {
goto error;
}
if (_omx_init_disable_ports(omx) < 0) {
goto error;
}
return omx;
error:
omx_encoder_destroy(omx);
return NULL;
}
void omx_encoder_destroy(omx_encoder_s *omx) {
LOG_INFO("Destroying OMX encoder ...");
omx_component_set_state(&omx->comp, OMX_StateIdle);
_omx_encoder_clear_ports(omx);
omx_component_set_state(&omx->comp, OMX_StateLoaded);
if (omx->i_handler_sem) {
vcos_semaphore_delete(&omx->handler_sem);
}
if (omx->i_encoder) {
OMX_ERRORTYPE error;
if ((error = OMX_FreeHandle(omx->comp)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't free OMX.broadcom.image_encode");
}
}
free(omx);
}
int omx_encoder_prepare(omx_encoder_s *omx, const frame_s *frame, unsigned quality) {
if (align_size(frame->width, 32) != frame->width && frame_get_padding(frame) == 0) {
LOG_ERROR("%u %u", frame->width, frame->stride);
LOG_ERROR("OMX encoder can't handle unaligned width");
return -2;
}
if (omx_component_set_state(&omx->comp, OMX_StateIdle) < 0) {
return -1;
}
if (_omx_encoder_clear_ports(omx) < 0) {
return -1;
}
if (_omx_setup_input(omx, frame) < 0) {
return -1;
}
if (_omx_setup_output(omx, quality) < 0) {
return -1;
}
if (omx_component_set_state(&omx->comp, OMX_StateExecuting) < 0) {
return -1;
}
return 0;
}
int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest) {
# define IN(_next) omx->input_buf->_next
# define OUT(_next) omx->output_buf->_next
OMX_ERRORTYPE error;
if ((error = OMX_FillThisBuffer(omx->comp, omx->output_buf)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Failed to request filling of the output buffer on encoder");
return -1;
}
dest->width = align_size(src->width, 32);
dest->used = 0;
omx->output_available = false;
omx->input_required = true;
size_t slice_size = (IN(nAllocLen) < src->used ? IN(nAllocLen) : src->used);
size_t pos = 0;
while (true) {
if (omx->failed) {
return -1;
}
if (omx->output_available) {
omx->output_available = false;
frame_append_data(dest, OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) {
OUT(nFlags) = 0;
break;
}
if ((error = OMX_FillThisBuffer(omx->comp, omx->output_buf)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Failed to request filling of the output buffer on encoder");
return -1;
}
}
if (omx->input_required) {
omx->input_required = false;
if (pos == src->used) {
continue;
}
memcpy(IN(pBuffer), src->data + pos, slice_size);
IN(nOffset) = 0;
IN(nFilledLen) = slice_size;
pos += slice_size;
if (pos + slice_size > src->used) {
slice_size = src->used - pos;
}
if ((error = OMX_EmptyThisBuffer(omx->comp, omx->input_buf)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Failed to request emptying of the input buffer on encoder");
return -1;
}
}
if (vcos_my_semwait("", &omx->handler_sem, 1) < 0) {
return -1;
}
}
# undef OUT
# undef IN
return 0;
}
static int _omx_init_component(omx_encoder_s *omx) {
OMX_ERRORTYPE error;
OMX_CALLBACKTYPE callbacks;
MEMSET_ZERO(callbacks);
callbacks.EventHandler = _omx_event_handler;
callbacks.EmptyBufferDone = _omx_input_required_handler;
callbacks.FillBufferDone = _omx_output_available_handler;
LOG_DEBUG("Initializing OMX.broadcom.image_encode ...");
if ((error = OMX_GetHandle(&omx->comp, "OMX.broadcom.image_encode", omx, &callbacks)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't initialize OMX.broadcom.image_encode");
return -1;
}
omx->i_encoder = true;
return 0;
}
static int _omx_init_disable_ports(omx_encoder_s *omx) {
OMX_ERRORTYPE error;
OMX_INDEXTYPE types[] = {
OMX_IndexParamAudioInit, OMX_IndexParamVideoInit,
OMX_IndexParamImageInit, OMX_IndexParamOtherInit,
};
OMX_PORT_PARAM_TYPE ports;
OMX_INIT_STRUCTURE(ports);
if ((error = OMX_GetParameter(omx->comp, OMX_IndexParamImageInit, &ports)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't OMX_GetParameter(OMX_IndexParamImageInit)");
return -1;
}
for (unsigned index = 0; index < 4; ++index) {
if ((error = OMX_GetParameter(omx->comp, types[index], &ports)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't OMX_GetParameter(types[%u])", index);
return -1;
}
for (OMX_U32 port = ports.nStartPortNumber; port < ports.nStartPortNumber + ports.nPorts; ++port) {
if (omx_component_disable_port(&omx->comp, port) < 0) {
return -1;
}
}
}
return 0;
}
static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame) {
LOG_DEBUG("Setting up OMX JPEG input port ...");
OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef;
if (omx_component_get_portdef(&omx->comp, &portdef, _INPUT_PORT) < 0) {
LOG_ERROR("... first");
return -1;
}
# define IFMT(_next) portdef.format.image._next
IFMT(nFrameWidth) = align_size(frame->width, 32);
IFMT(nFrameHeight) = frame->height;
IFMT(nStride) = align_size(frame->width, 32) << 1;
IFMT(nSliceHeight) = align_size(frame->height, 16);
IFMT(bFlagErrorConcealment) = OMX_FALSE;
IFMT(eCompressionFormat) = OMX_IMAGE_CodingUnused;
portdef.nBufferSize = ((frame->width * frame->height) << 1) * 2;
switch (frame->format) {
// https://www.fourcc.org/yuv.php
// Also see comments inside OMX_IVCommon.h
case V4L2_PIX_FMT_YUYV: IFMT(eColorFormat) = OMX_COLOR_FormatYCbYCr; break;
case V4L2_PIX_FMT_UYVY: IFMT(eColorFormat) = OMX_COLOR_FormatCbYCrY; break;
case V4L2_PIX_FMT_RGB565: IFMT(eColorFormat) = OMX_COLOR_Format16bitRGB565; break;
case V4L2_PIX_FMT_RGB24: IFMT(eColorFormat) = OMX_COLOR_Format24bitRGB888; break;
// TODO: найти устройство с RGB565 и протестить его.
// FIXME: RGB24 не работает нормально, нижняя половина экрана зеленая.
// FIXME: Китайский EasyCap тоже не работает, мусор на экране.
// Вероятно обе проблемы вызваны некорректной реализацией OMX на пае.
default: assert(0 && "Unsupported format");
}
# undef IFMT
if (omx_component_set_portdef(&omx->comp, &portdef) < 0) {
return -1;
}
if (omx_component_get_portdef(&omx->comp, &portdef, _INPUT_PORT) < 0) {
LOG_ERROR("... second");
return -1;
}
if (omx_component_enable_port(&omx->comp, _INPUT_PORT) < 0) {
return -1;
}
omx->i_input_port_enabled = true;
if ((error = OMX_AllocateBuffer(omx->comp, &omx->input_buf, _INPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't allocate OMX JPEG input buffer");
return -1;
}
return 0;
}
static int _omx_setup_output(omx_encoder_s *omx, unsigned quality) {
LOG_DEBUG("Setting up OMX JPEG output port ...");
OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef;
if (omx_component_get_portdef(&omx->comp, &portdef, _OUTPUT_PORT) < 0) {
LOG_ERROR("... first");
return -1;
}
# define OFMT(_next) portdef.format.image._next
OFMT(bFlagErrorConcealment) = OMX_FALSE;
OFMT(eCompressionFormat) = OMX_IMAGE_CodingJPEG;
OFMT(eColorFormat) = OMX_COLOR_FormatYCbYCr;
# undef OFMT
if (omx_component_set_portdef(&omx->comp, &portdef) < 0) {
return -1;
}
if (omx_component_get_portdef(&omx->comp, &portdef, _OUTPUT_PORT) < 0) {
LOG_ERROR("... second");
return -1;
}
# define SET_PARAM(_key, _value) { \
if ((error = OMX_SetParameter(omx->comp, OMX_IndexParam##_key, _value)) != OMX_ErrorNone) { \
LOG_ERROR_OMX(error, "Can't set OMX param %s", #_key); \
return -1; \
} \
}
OMX_CONFIG_BOOLEANTYPE exif;
OMX_INIT_STRUCTURE(exif);
exif.bEnabled = OMX_FALSE;
SET_PARAM(BrcmDisableEXIF, &exif);
OMX_PARAM_IJGSCALINGTYPE ijg;
OMX_INIT_STRUCTURE(ijg);
ijg.nPortIndex = _OUTPUT_PORT;
ijg.bEnabled = OMX_TRUE;
SET_PARAM(BrcmEnableIJGTableScaling, &ijg);
OMX_IMAGE_PARAM_QFACTORTYPE qfactor;
OMX_INIT_STRUCTURE(qfactor);
qfactor.nPortIndex = _OUTPUT_PORT;
qfactor.nQFactor = quality;
SET_PARAM(QFactor, &qfactor);
# undef SET_PARAM
if (omx_component_enable_port(&omx->comp, _OUTPUT_PORT) < 0) {
return -1;
}
omx->i_output_port_enabled = true;
if ((error = OMX_AllocateBuffer(omx->comp, &omx->output_buf, _OUTPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't allocate OMX JPEG output buffer");
return -1;
}
return 0;
}
static int _omx_encoder_clear_ports(omx_encoder_s *omx) {
OMX_ERRORTYPE error;
int retval = 0;
if (omx->i_output_port_enabled) {
retval -= omx_component_disable_port(&omx->comp, _OUTPUT_PORT);
omx->i_output_port_enabled = false;
}
if (omx->i_input_port_enabled) {
retval -= omx_component_disable_port(&omx->comp, _INPUT_PORT);
omx->i_input_port_enabled = false;
}
if (omx->input_buf) {
if ((error = OMX_FreeBuffer(omx->comp, _INPUT_PORT, omx->input_buf)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't free OMX JPEG input buffer");
// retval -= 1;
}
omx->input_buf = NULL;
}
if (omx->output_buf) {
if ((error = OMX_FreeBuffer(omx->comp, _OUTPUT_PORT, omx->output_buf)) != OMX_ErrorNone) {
LOG_ERROR_OMX(error, "Can't free OMX JPEG output buffer");
// retval -= 1;
}
omx->output_buf = NULL;
}
return retval;
}
static OMX_ERRORTYPE _omx_event_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1,
UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data) {
// OMX calls this handler for all the events it emits
omx_encoder_s *omx = (omx_encoder_s *)v_omx;
if (event == OMX_EventError) {
LOG_ERROR_OMX((OMX_ERRORTYPE)data1, "OMX error event received");
omx->failed = true;
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
}
return OMX_ErrorNone;
}
static OMX_ERRORTYPE _omx_input_required_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buf) {
// Called by OMX when the encoder component requires
// the input buffer to be filled with RAW image data
omx_encoder_s *omx = (omx_encoder_s *)v_omx;
omx->input_required = true;
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE _omx_output_available_handler(
UNUSED OMX_HANDLETYPE comp,
OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buf) {
// Called by OMX when the encoder component has filled
// the output buffer with JPEG data
omx_encoder_s *omx = (omx_encoder_s *)v_omx;
omx->output_available = true;
assert(vcos_semaphore_post(&omx->handler_sem) == VCOS_SUCCESS);
return OMX_ErrorNone;
}

View File

@ -1,73 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <linux/videodev2.h>
#include <IL/OMX_Core.h>
#include <IL/OMX_Component.h>
#include <IL/OMX_Broadcom.h>
#include <interface/vcos/vcos_semaphore.h>
#include "../../../libs/tools.h"
#include "../../../libs/logging.h"
#include "../../../libs/frame.h"
#include "vcos.h"
#include "formatters.h"
#include "component.h"
#ifndef CFG_OMX_MAX_ENCODERS
# define CFG_OMX_MAX_ENCODERS 3 // Raspberry Pi limitation
#endif
#define OMX_MAX_ENCODERS ((unsigned)(CFG_OMX_MAX_ENCODERS))
typedef struct {
OMX_HANDLETYPE comp;
OMX_BUFFERHEADERTYPE *input_buf;
OMX_BUFFERHEADERTYPE *output_buf;
bool input_required;
bool output_available;
bool failed;
VCOS_SEMAPHORE_T handler_sem;
bool i_handler_sem;
bool i_encoder;
bool i_input_port_enabled;
bool i_output_port_enabled;
} omx_encoder_s;
omx_encoder_s *omx_encoder_init(void);
void omx_encoder_destroy(omx_encoder_s *omx);
int omx_encoder_prepare(omx_encoder_s *omx, const frame_s *frame, unsigned quality);
int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest);

View File

@ -1,95 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "formatters.h"
#define CASE_TO_STRING(_value) \
case _value: { return #_value; }
const char *omx_error_to_string(OMX_ERRORTYPE error) {
switch (error) {
CASE_TO_STRING(OMX_ErrorNone);
CASE_TO_STRING(OMX_ErrorInsufficientResources);
CASE_TO_STRING(OMX_ErrorUndefined);
CASE_TO_STRING(OMX_ErrorInvalidComponentName);
CASE_TO_STRING(OMX_ErrorComponentNotFound);
CASE_TO_STRING(OMX_ErrorInvalidComponent);
CASE_TO_STRING(OMX_ErrorBadParameter);
CASE_TO_STRING(OMX_ErrorNotImplemented);
CASE_TO_STRING(OMX_ErrorUnderflow);
CASE_TO_STRING(OMX_ErrorOverflow);
CASE_TO_STRING(OMX_ErrorHardware);
CASE_TO_STRING(OMX_ErrorInvalidState);
CASE_TO_STRING(OMX_ErrorStreamCorrupt);
CASE_TO_STRING(OMX_ErrorPortsNotCompatible);
CASE_TO_STRING(OMX_ErrorResourcesLost);
CASE_TO_STRING(OMX_ErrorNoMore);
CASE_TO_STRING(OMX_ErrorVersionMismatch);
CASE_TO_STRING(OMX_ErrorNotReady);
CASE_TO_STRING(OMX_ErrorTimeout);
CASE_TO_STRING(OMX_ErrorSameState);
CASE_TO_STRING(OMX_ErrorResourcesPreempted);
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringAllocation);
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringDeallocation);
CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringStop);
CASE_TO_STRING(OMX_ErrorIncorrectStateTransition);
CASE_TO_STRING(OMX_ErrorIncorrectStateOperation);
CASE_TO_STRING(OMX_ErrorUnsupportedSetting);
CASE_TO_STRING(OMX_ErrorUnsupportedIndex);
CASE_TO_STRING(OMX_ErrorBadPortIndex);
CASE_TO_STRING(OMX_ErrorPortUnpopulated);
CASE_TO_STRING(OMX_ErrorComponentSuspended);
CASE_TO_STRING(OMX_ErrorDynamicResourcesUnavailable);
CASE_TO_STRING(OMX_ErrorMbErrorsInFrame);
CASE_TO_STRING(OMX_ErrorFormatNotDetected);
CASE_TO_STRING(OMX_ErrorContentPipeOpenFailed);
CASE_TO_STRING(OMX_ErrorContentPipeCreationFailed);
CASE_TO_STRING(OMX_ErrorSeperateTablesUsed);
CASE_TO_STRING(OMX_ErrorTunnelingUnsupported);
CASE_TO_STRING(OMX_ErrorKhronosExtensions);
CASE_TO_STRING(OMX_ErrorVendorStartUnused);
CASE_TO_STRING(OMX_ErrorDiskFull);
CASE_TO_STRING(OMX_ErrorMaxFileSize);
CASE_TO_STRING(OMX_ErrorDrmUnauthorised);
CASE_TO_STRING(OMX_ErrorDrmExpired);
CASE_TO_STRING(OMX_ErrorDrmGeneral);
default: return "Unknown OMX error";
}
}
const char *omx_state_to_string(OMX_STATETYPE state) {
switch (state) {
CASE_TO_STRING(OMX_StateInvalid);
CASE_TO_STRING(OMX_StateLoaded);
CASE_TO_STRING(OMX_StateIdle);
CASE_TO_STRING(OMX_StateExecuting);
CASE_TO_STRING(OMX_StatePause);
CASE_TO_STRING(OMX_StateWaitForResources);
// cppcheck-suppress constArgument
// cppcheck-suppress knownArgument
default: break;
}
assert(0 && "Unsupported OMX state");
}
#undef CASE_TO_STRING

View File

@ -1,42 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include <stdio.h>
#include <assert.h>
#include <IL/OMX_IVCommon.h>
#include <IL/OMX_Core.h>
#include <IL/OMX_Image.h>
#include "../../../libs/tools.h"
#include "../../../libs/logging.h"
#define LOG_ERROR_OMX(_error, _msg, ...) { \
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, omx_error_to_string(_error)); \
}
const char *omx_error_to_string(OMX_ERRORTYPE error);
const char *omx_state_to_string(OMX_STATETYPE state);

View File

@ -1,55 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "vcos.h"
int vcos_my_semwait(const char *prefix, VCOS_SEMAPHORE_T *sem, long double timeout) {
// 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
// - The current approach is an ugly busyloop
// Три стула.
long double deadline_ts = get_now_monotonic() + timeout;
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) {
break;
}
if (usleep(1000) < 0) {
break;
}
}
switch (sem_status) {
case VCOS_EAGAIN: LOG_ERROR("%sCan't wait VCOS semaphore: EAGAIN (timeout)", prefix); break;
case VCOS_EINVAL: LOG_ERROR("%sCan't wait VCOS semaphore: EINVAL", prefix); break;
default: LOG_ERROR("%sCan't wait VCOS semaphore: %d", prefix, sem_status); break;
}
return -1;
}

View File

@ -1,31 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include <interface/vcos/vcos_semaphore.h>
#include "../../../libs/tools.h"
#include "../../../libs/logging.h"
int vcos_my_semwait(const char *prefix, VCOS_SEMAPHORE_T *sem, long double timeout);

View File

@ -159,7 +159,7 @@ static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
}
{
if (enc->fps > 0) { // TODO: Check this for MJPEG
struct v4l2_streamparm setfps = {0};
setfps.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
setfps.parm.output.timeperframe.numerator = 1;
@ -316,7 +316,7 @@ int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest,
frame_copy_meta(src, dest);
dest->encode_begin_ts = get_now_monotonic();
dest->format = enc->output_format;
dest->format = (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format);
dest->stride = 0;
force_key = (force_key || enc->last_online != src->online);

View File

@ -35,10 +35,6 @@
#include <signal.h>
#include <pthread.h>
#ifdef WITH_OMX
# include <bcm_host.h>
# include <IL/OMX_Core.h>
#endif
#include "../libs/tools.h"
#include "../libs/threading.h"
@ -124,23 +120,7 @@ int main(int argc, char *argv[]) {
stream_s *stream = stream_init(dev, enc);
server_s *server = server_init(stream);
# ifdef WITH_OMX
bool i_bcm_host = false;
OMX_ERRORTYPE omx_error = OMX_ErrorUndefined;
# endif
if ((exit_code = options_parse(options, dev, enc, stream, server)) == 0) {
# ifdef WITH_OMX
if (enc->type == ENCODER_TYPE_OMX) {
bcm_host_init();
i_bcm_host = true;
if ((omx_error = OMX_Init()) != OMX_ErrorNone) {
LOG_ERROR_OMX(omx_error, "Can't initialize OMX Core; forced CPU encoder");
enc->type = ENCODER_TYPE_CPU;
}
}
# endif
# ifdef WITH_GPIO
gpio_init();
# endif
@ -177,15 +157,6 @@ int main(int argc, char *argv[]) {
device_destroy(dev);
options_destroy(options);
# ifdef WITH_OMX
if (omx_error == OMX_ErrorNone) {
OMX_Deinit();
}
if (i_bcm_host) {
bcm_host_deinit();
}
# endif
if (exit_code == 0) {
LOG_INFO("Bye-bye");
}

View File

@ -38,9 +38,7 @@ enum _OPT_VALUES {
_O_WORKERS = 'w',
_O_QUALITY = 'q',
_O_ENCODER = 'c',
# ifdef WITH_OMX
_O_GLITCHED_RESOLUTIONS = 'g',
# endif
_O_GLITCHED_RESOLUTIONS = 'g', // Deprecated
_O_BLANK = 'k',
_O_LAST_AS_BLANK = 'K',
_O_SLOWDOWN = 'l',
@ -142,9 +140,7 @@ static const struct option _LONG_OPTS[] = {
{"workers", required_argument, NULL, _O_WORKERS},
{"quality", required_argument, NULL, _O_QUALITY},
{"encoder", required_argument, NULL, _O_ENCODER},
# ifdef WITH_OMX
{"glitched-resolutions", required_argument, NULL, _O_GLITCHED_RESOLUTIONS},
# endif
{"glitched-resolutions", required_argument, NULL, _O_GLITCHED_RESOLUTIONS}, // Deprecated
{"blank", required_argument, NULL, _O_BLANK},
{"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK},
{"slowdown", no_argument, NULL, _O_SLOWDOWN},
@ -378,9 +374,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
case _O_WORKERS: OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
case _O_QUALITY: OPT_NUMBER("--quality", dev->jpeg_quality, 1, 100, 0);
case _O_ENCODER: OPT_PARSE("encoder type", enc->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
# ifdef WITH_OMX
case _O_GLITCHED_RESOLUTIONS: break;
# endif
case _O_GLITCHED_RESOLUTIONS: break; // Deprecated
case _O_BLANK: OPT_SET(blank_path, optarg);
case _O_LAST_AS_BLANK: OPT_NUMBER("--last-as-blank", stream->last_as_blank, 0, 86400, 0);
case _O_SLOWDOWN: OPT_SET(stream->slowdown, true);
@ -542,12 +536,6 @@ static int _parse_resolution(const char *str, unsigned *width, unsigned *height,
}
static void _features(void) {
# ifdef WITH_OMX
puts("+ WITH_OMX");
# else
puts("- WITH_OMX");
# endif
# ifdef WITH_GPIO
puts("+ WITH_GPIO");
# else
@ -617,14 +605,10 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.");
SAY(" Available:");
SAY(" * CPU ── Software MJPG encoding (default);");
# ifdef WITH_OMX
SAY(" * OMX ── GPU hardware accelerated MJPG encoding with OpenMax;");
# endif
SAY(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware.");
SAY(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware;");
SAY(" * V4L2 ─ GPU-accelerated MJPG encoding using V4L2 M2M interface;");
SAY(" * NOOP ─ Don't compress MJPG stream (do nothing).\n");
# ifdef WITH_OMX
SAY(" -g|--glitched-resolutions <WxH,...> ─ It doesn't do anything. Still here for compatibility.\n");
# endif
SAY(" -k|--blank <path> ─────────────────── Path to JPEG file that will be shown when the device is disconnected");
SAY(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n");
SAY(" -K|--last-as-blank <sec> ──────────── Show the last frame received from the camera after it was disconnected,");

View File

@ -277,7 +277,7 @@ static workers_pool_s *_stream_init_one(stream_s *stream) {
if (device_open(stream->dev) < 0) {
goto error;
}
if (RUN(h264) && !is_jpeg(stream->dev->run->format)) {
if (stream->enc->type == ENCODER_TYPE_V4L2 || (RUN(h264) && !is_jpeg(stream->dev->run->format))) {
device_export_to_dma(stream->dev);
}
if (device_switch_capturing(stream->dev, true) < 0) {