supported hw mjpeg format

This commit is contained in:
Devaev Maxim
2019-03-03 15:39:32 +03:00
parent cc25abcc00
commit 1541921070
10 changed files with 159 additions and 27 deletions

View File

@@ -55,6 +55,8 @@ static const struct {
{"YUYV", V4L2_PIX_FMT_YUYV},
{"UYVY", V4L2_PIX_FMT_UYVY},
{"RGB565", V4L2_PIX_FMT_RGB565},
{"JPEG", V4L2_PIX_FMT_MJPEG},
{"JPEG", V4L2_PIX_FMT_JPEG},
};

View File

@@ -39,7 +39,7 @@
#define STANDARDS_STR "UNKNOWN, PAL, NTSC, SECAM"
#define FORMAT_UNKNOWN -1
#define FORMATS_STR "YUYV, UYVY, RGB565"
#define FORMATS_STR "YUYV, UYVY, RGB565, JPEG"
struct hw_buffer_t {
@@ -62,7 +62,7 @@ struct device_runtime_t {
unsigned height;
unsigned format;
unsigned n_buffers;
unsigned n_workers;
// unsigned n_workers; // FIXME
struct hw_buffer_t *hw_buffers;
struct picture_t *pictures;
unsigned long max_picture_size;

View File

@@ -23,12 +23,15 @@
#include <strings.h>
#include <assert.h>
#include <linux/videodev2.h>
#include "tools.h"
#include "logging.h"
#include "device.h"
#include "encoder.h"
#include "jpeg/encoder.h"
#include "hw/encoder.h"
#ifdef OMX_ENCODER
# include "omx/encoder.h"
@@ -40,6 +43,7 @@ static const struct {
const enum encoder_type_t type;
} _ENCODER_TYPES[] = {
{"CPU", ENCODER_TYPE_CPU},
{"HW", ENCODER_TYPE_HW},
# ifdef OMX_ENCODER
{"OMX", ENCODER_TYPE_OMX},
# endif
@@ -52,10 +56,12 @@ struct encoder_t *encoder_init() {
A_CALLOC(run, 1);
run->type = ENCODER_TYPE_CPU;
run->quality = 80;
A_PTHREAD_M_INIT(&run->mutex);
A_CALLOC(encoder, 1);
encoder->type = run->type;
encoder->quality = 80;
encoder->quality = run->quality;
encoder->run = run;
return encoder;
}
@@ -66,16 +72,16 @@ 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;
if (encoder->run->type != ENCODER_TYPE_CPU) {
LOG_DEBUG("Initializing encoder ...");
}
encoder->run->quality = encoder->quality;
LOG_INFO("Using JPEG quality: %u%%", encoder->quality);
# ifdef OMX_ENCODER
if (encoder->run->type == ENCODER_TYPE_OMX) {
LOG_DEBUG("Preparing OMX encoder ...");
if (dev->n_workers > OMX_MAX_ENCODERS) {
LOG_INFO(
"OMX-based encoder can only work with %u worker threads; forced --workers=%u",
@@ -101,6 +107,7 @@ void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
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
}
@@ -115,6 +122,7 @@ void encoder_destroy(struct encoder_t *encoder) {
free(encoder->run->omxs);
}
# endif
A_PTHREAD_M_DESTROY(&encoder->run->mutex);
free(encoder->run);
free(encoder);
}
@@ -133,11 +141,33 @@ enum encoder_type_t encoder_parse_type(const char *str) {
void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
if (
(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 encoder because the input format is (M)JPEG");
A_PTHREAD_M_LOCK(&encoder->run->mutex);
encoder->run->type = ENCODER_TYPE_HW;
A_PTHREAD_M_UNLOCK(&encoder->run->mutex);
}
if (encoder->run->type == ENCODER_TYPE_HW) {
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
LOG_INFO("Switching to CPU encoder because the input format is not (M)JPEG");
goto use_fallback;
}
if (hw_encoder_prepare_live(dev, encoder->quality) < 0) {
A_PTHREAD_M_LOCK(&encoder->run->mutex);
encoder->run->quality = 0;
A_PTHREAD_M_UNLOCK(&encoder->run->mutex);
}
}
#pragma GCC diagnostic pop
# ifdef OMX_ENCODER
if (encoder->run->type == ENCODER_TYPE_OMX) {
else if (encoder->run->type == ENCODER_TYPE_OMX) {
for (unsigned index = 0; index < encoder->run->n_omxs; ++index) {
if (omx_encoder_prepare_live(encoder->run->omxs[index], dev, encoder->quality) < 0) {
LOG_ERROR("Can't prepare OMX encoder, falling back to CPU");
goto use_fallback;
}
}
@@ -149,8 +179,10 @@ void encoder_prepare_live(struct encoder_t *encoder, struct device_t *dev) {
# pragma GCC diagnostic ignored "-Wunused-label"
# pragma GCC diagnostic push
use_fallback:
LOG_ERROR("Can't prepare selected encoder, falling back to CPU");
A_PTHREAD_M_LOCK(&encoder->run->mutex);
encoder->run->type = ENCODER_TYPE_CPU;
encoder->run->quality = encoder->quality;
A_PTHREAD_M_UNLOCK(&encoder->run->mutex);
# pragma GCC diagnostic pop
}
@@ -163,11 +195,13 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
if (encoder->run->type == ENCODER_TYPE_CPU) {
jpeg_encoder_compress_buffer(dev, buf_index, encoder->quality);
} else if (encoder->run->type == ENCODER_TYPE_HW) {
hw_encoder_compress_buffer(dev, buf_index);
}
# ifdef OMX_ENCODER
else if (encoder->run->type == ENCODER_TYPE_OMX) {
if (omx_encoder_compress_buffer(encoder->run->omxs[worker_number], dev, buf_index) < 0) {
goto error;
goto use_fallback;
}
}
# endif
@@ -176,9 +210,12 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
# pragma GCC diagnostic ignored "-Wunused-label"
# pragma GCC diagnostic push
error:
LOG_INFO("HW compressing error, falling back to CPU");
use_fallback:
LOG_INFO("Error while compressing, falling back to CPU");
A_PTHREAD_M_LOCK(&encoder->run->mutex);
encoder->run->type = ENCODER_TYPE_CPU;
encoder->run->quality = encoder->quality;
A_PTHREAD_M_UNLOCK(&encoder->run->mutex);
return -1;
# pragma GCC diagnostic pop
}

View File

@@ -22,6 +22,8 @@
#pragma once
#include "pthread.h"
#include "tools.h"
#include "device.h"
@@ -34,19 +36,23 @@
#define ENCODER_TYPES_STR \
"CPU" \
"CPU, HW" \
ENCODER_TYPES_OMX_HINT
enum encoder_type_t {
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
ENCODER_TYPE_CPU,
ENCODER_TYPE_HW,
#ifdef OMX_ENCODER
ENCODER_TYPE_OMX,
#endif
};
struct encoder_runtime_t {
enum encoder_type_t type;
enum encoder_type_t type;
unsigned quality;
pthread_mutex_t mutex;
#ifdef OMX_ENCODER
unsigned n_omxs;
struct omx_encoder_t **omxs;

View File

@@ -47,6 +47,7 @@
#include "tools.h"
#include "logging.h"
#include "encoder.h"
#include "stream.h"
#include "http.h"
@@ -259,6 +260,11 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
PROCESS_HEAD_REQUEST;
A_PTHREAD_M_LOCK(&server->run->stream->encoder->run->mutex);
enum encoder_type_t encoder_run_type = server->run->stream->encoder->run->type;
unsigned encoder_run_quality = server->run->stream->encoder->run->quality;
A_PTHREAD_M_UNLOCK(&server->run->stream->encoder->run->mutex);
assert((buf = evbuffer_new()));
assert(evbuffer_add_printf(buf,
"{\"ok\": true, \"result\": {"
@@ -266,8 +272,8 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
bool_to_string(server->run->stream->encoder->type != server->run->stream->encoder->run->type),
server->run->stream->encoder->quality,
bool_to_string(server->run->stream->encoder->type != encoder_run_type),
encoder_run_quality,
(server->fake_width ? server->fake_width : server->run->exposed->width),
(server->fake_height ? server->fake_height : server->run->exposed->height),
bool_to_string(server->run->exposed->online),

58
src/hw/encoder.c Normal file
View File

@@ -0,0 +1,58 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 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 <string.h>
#include <assert.h>
#include <linux/videodev2.h>
#include "../tools.h"
#include "../logging.h"
#include "../xioctl.h"
#include "../device.h"
int hw_encoder_prepare_live(struct device_t *dev, unsigned quality) {
struct v4l2_jpegcompression comp;
MEMSET_ZERO(comp);
if (xioctl(dev->run->fd, VIDIOC_G_JPEGCOMP, &comp) < 0) {
LOG_ERROR("Can't query HW JPEG compressor params and set quality (unsupported)");
return -1;
}
comp.quality = quality;
if (xioctl(dev->run->fd, VIDIOC_S_JPEGCOMP, &comp) < 0) {
LOG_ERROR("Can't set HW JPEG compressor quality (unsopported)");
return -1;
}
return 0;
}
void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) {
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
assert(0 && "Unsupported input format for HW JPEG compressor");
}
assert(dev->run->pictures[index].allocated >= dev->run->hw_buffers[index].length);
memcpy(dev->run->pictures[index].data, dev->run->hw_buffers[index].start, dev->run->hw_buffers[index].length);
dev->run->pictures[index].size = dev->run->hw_buffers[index].length;
}

29
src/hw/encoder.h Normal file
View File

@@ -0,0 +1,29 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018 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 "../device.h"
int hw_encoder_prepare_live(struct device_t *dev, unsigned quality);
void hw_encoder_compress_buffer(struct device_t *dev, unsigned index);