supported /dev/video21

This commit is contained in:
Maxim Devaev
2021-12-10 15:57:03 +03:00
parent 2b969dd20d
commit aa03e1610f
7 changed files with 90 additions and 55 deletions

View File

@@ -27,10 +27,11 @@ static const struct {
const char *name;
const encoder_type_e type;
} _ENCODER_TYPES[] = {
{"CPU", ENCODER_TYPE_CPU},
{"HW", ENCODER_TYPE_HW},
{"M2M", ENCODER_TYPE_M2M},
{"NOOP", ENCODER_TYPE_NOOP},
{"CPU", ENCODER_TYPE_CPU},
{"HW", ENCODER_TYPE_HW},
{"M2M-MJPEG", ENCODER_TYPE_M2M_MJPEG},
{"M2M-JPEG", ENCODER_TYPE_M2M_JPEG},
{"NOOP", ENCODER_TYPE_NOOP},
};
@@ -53,7 +54,6 @@ encoder_s *encoder_init(void) {
A_CALLOC(enc, 1);
enc->type = run->type;
enc->n_workers = get_cores_available();
enc->m2m_path = "/dev/video11";
enc->run = run;
return enc;
}
@@ -74,7 +74,7 @@ void encoder_destroy(encoder_s *enc) {
encoder_type_e encoder_parse_type(const char *str) {
if (!strcasecmp(str, "OMX")) {
return ENCODER_TYPE_M2M; // Just for compatibility
return ENCODER_TYPE_M2M_JPEG; // Just for compatibility
}
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
@@ -114,30 +114,42 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
quality = DR(jpeg_quality);
n_workers = 1;
} else if (type == ENCODER_TYPE_M2M) {
} else if (type == ENCODER_TYPE_M2M_MJPEG || type == ENCODER_TYPE_M2M_JPEG) {
LOG_DEBUG("Preparing M2M encoder ...");
if (ER(m2ms) == NULL) {
A_CALLOC(ER(m2ms), n_workers);
}
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
if (ER(n_m2ms) < n_workers) {
double b_min = ENCODER_M2M_BITRATE_MIN;
double b_max = ENCODER_M2M_BITRATE_MAX;
double step = ENCODER_M2M_BITRATE_STEP;
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
bitrate = step * round(bitrate / step);
LOG_VERBOSE("Using JPEG M2M bitrate: %u%% -> %u Kbps", quality, (unsigned)bitrate);
if (type == ENCODER_TYPE_M2M_MJPEG) {
double b_min = ENCODER_M2M_BITRATE_MIN;
double b_max = ENCODER_M2M_BITRATE_MAX;
double step = ENCODER_M2M_BITRATE_STEP;
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
bitrate = step * round(bitrate / step);
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
assert(bitrate > 0);
char name[32];
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
m2m_option_s options[] = {
{"BITRATE", true, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate * 1000},
{NULL, false, 0, 0},
};
ER(m2ms[ER(n_m2ms)]) = m2m_encoder_init(name, enc->m2m_path, V4L2_PIX_FMT_MJPEG, 0, options);
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
assert(bitrate > 0);
char name[32];
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
m2m_option_s options[] = {
{"BITRATE", true, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate * 1000},
{NULL, false, 0, 0},
};
ER(m2ms[ER(n_m2ms)]) = m2m_encoder_init(name, enc->m2m_path, V4L2_PIX_FMT_MJPEG, 0, options);
}
} else {
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
char name[32];
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
m2m_option_s options[] = {
{"QUALITY", true, V4L2_CID_JPEG_COMPRESSION_QUALITY, quality},
{NULL, false, 0, 0},
};
ER(m2ms[ER(n_m2ms)]) = m2m_encoder_init(name, enc->m2m_path, V4L2_PIX_FMT_JPEG, 0, options);
}
}
}
@@ -225,7 +237,7 @@ static bool _worker_run_job(worker_s *wr) {
LOG_VERBOSE("Compressing buffer using HW (just copying)");
hw_encoder_compress(src, dest);
} else if (ER(type) == ENCODER_TYPE_M2M) {
} else if (ER(type) == ENCODER_TYPE_M2M_MJPEG || ER(type) == ENCODER_TYPE_M2M_JPEG) {
LOG_VERBOSE("Compressing buffer using M2M");
if (m2m_encoder_ensure_ready(ER(m2ms[wr->number]), src) < 0) {
goto error;

View File

@@ -60,13 +60,14 @@
#define ENCODER_M2M_BITRATE_STEP ((unsigned)CFG_ENCODER_M2M_BITRATE_STEP)
#define ENCODER_TYPES_STR "CPU, HW, M2M, NOOP"
#define ENCODER_TYPES_STR "CPU, HW, M2M-MJPEG, M2M-JPEG, NOOP"
typedef enum {
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
ENCODER_TYPE_CPU,
ENCODER_TYPE_HW,
ENCODER_TYPE_M2M,
ENCODER_TYPE_M2M_MJPEG,
ENCODER_TYPE_M2M_JPEG,
ENCODER_TYPE_NOOP,
} encoder_type_e;

View File

@@ -43,12 +43,18 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
m2m_encoder_s *m2m_encoder_init(const char *name, const char *path, unsigned format, unsigned fps, m2m_option_s *options) {
assert(format == V4L2_PIX_FMT_H264 || format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
LOG_INFO("%s: Initializing encoder ...", name);
m2m_encoder_s *enc;
A_CALLOC(enc, 1);
assert(enc->name = strdup(name));
assert(enc->path = strdup(path));
if (path == NULL) {
assert(enc->path = strdup(format == V4L2_PIX_FMT_JPEG ? "/dev/video21" : "/dev/video11"));
} else {
assert(enc->path = strdup(path));
}
enc->output_format = format;
enc->fps = fps;
enc->last_online = -1;
@@ -153,8 +159,8 @@ static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
fmt.fmt.pix_mp.num_planes = 1;
fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 512 << 10;
//fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
//fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 512 << 10;
E_LOG_DEBUG("Configuring OUTPUT format ...");
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
if (fmt.fmt.pix_mp.pixelformat != enc->output_format) {
@@ -323,7 +329,7 @@ int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest,
frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
force_key = (force_key || enc->last_online != src->online);
force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || enc->last_online != src->online));
if (_m2m_encoder_compress_raw(enc, src, dest, force_key) < 0) {
_m2m_encoder_cleanup(enc);

View File

@@ -96,6 +96,7 @@ enum _OPT_VALUES {
ADD_SINK(H264_SINK)
_O_H264_BITRATE,
_O_H264_GOP,
_O_H264_M2M_DEVICE,
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -191,6 +192,7 @@ static const struct option _LONG_OPTS[] = {
ADD_SINK("h264-", H264_SINK)
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
{"h264-gop", required_argument, NULL, _O_H264_GOP},
{"h264-m2m-device", required_argument, NULL, _O_H264_M2M_DEVICE},
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -437,8 +439,9 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
ADD_SINK("", sink, SINK)
ADD_SINK("raw-", raw_sink, RAW_SINK)
ADD_SINK("h264-", h264_sink, H264_SINK)
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 25000, 0);
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 25000, 0);
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -569,7 +572,7 @@ static void _features(void) {
static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server) {
# define SAY(_msg, ...) fprintf(fp, _msg "\n", ##__VA_ARGS__)
SAY("\nuStreamer - Lightweight and fast MJPG-HTTP streamer");
SAY("\nuStreamer - Lightweight and fast MJPEG-HTTP streamer");
SAY("═══════════════════════════════════════════════════");
SAY("Version: %s; license: GPLv3", VERSION);
SAY("Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com>\n");
@@ -600,14 +603,15 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" Note: If HW encoding is used (JPEG source format selected),");
SAY(" this parameter attempts to configure the camera");
SAY(" or capture device hardware's internal encoder.");
SAY(" It does not re-encode MJPG to MJPG to change the quality level");
SAY(" for sources that already output MJPG.\n");
SAY(" It does not re-encode MJPEG to MJPEG to change the quality level");
SAY(" for sources that already output MJPEG.\n");
SAY(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.");
SAY(" Available:");
SAY(" * CPU ── Software MJPG encoding (default);");
SAY(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware;");
SAY(" * M2M ── GPU-accelerated MJPG encoding using V4L2 M2M interface;");
SAY(" * NOOP ─ Don't compress MJPG stream (do nothing).\n");
SAY(" * CPU ──────── Software MJPEG encoding (default);");
SAY(" * HW ───────── Use pre-encoded MJPEG frames directly from camera hardware;");
SAY(" * M2M-MJPEG ── GPU-accelerated MJPEG encoding using V4L2 M2M interface;");
SAY(" * M2M-JPEG ─── GPU-accelerated JPEG encoding using V4L2 M2M interface;");
SAY(" * NOOP ─────── Don't compress MJPEG stream (do nothing).\n");
SAY(" -g|--glitched-resolutions <WxH,...> ─ It doesn't do anything. Still here for compatibility.\n");
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");
@@ -621,7 +625,7 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", dev->timeout);
SAY(" --device-error-delay <sec> ────────── Delay before trying to connect to the device again");
SAY(" after an error (timeout for example). Default: %u.\n", stream->error_delay);
SAY(" --m2m-device </dev/path> ──────────── Path to V4L2 M2M encoder device. Default: %s.\n", enc->m2m_path);
SAY(" --m2m-device </dev/path> ──────────── Path to V4L2 M2M encoder device. Default: auto select.\n");
SAY("Image control options:");
SAY("══════════════════════");
SAY(" --image-default ────────────────────── Reset all image settings below to default. Default: no change.\n");
@@ -673,8 +677,9 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
ADD_SINK("JPEG", "")
ADD_SINK("RAW", "raw-")
ADD_SINK("H264", "h264-")
SAY(" --h264-bitrate <kbps> ──────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
SAY(" --h264-gop <N> ─────────────── Intarval between keyframes. Default: %u.\n", stream->h264_gop);
SAY(" --h264-bitrate <kbps> ──────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
SAY(" --h264-gop <N> ─────────────── Intarval between keyframes. Default: %u.\n", stream->h264_gop);
SAY(" --h264-m2m-device </dev/path> ─ Path to V4L2 M2M encoder device. Default: auto select.\n");
# undef ADD_SINK
# ifdef WITH_GPIO
SAY("GPIO options:");

View File

@@ -83,7 +83,7 @@ void stream_loop(stream_s *stream) {
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
if (stream->h264_sink) {
RUN(h264) = h264_stream_init(stream->h264_sink, stream->enc->m2m_path, stream->h264_bitrate, stream->h264_gop);
RUN(h264) = h264_stream_init(stream->h264_sink, stream->h264_m2m_path, stream->h264_bitrate, stream->h264_gop);
}
for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
@@ -276,7 +276,11 @@ static workers_pool_s *_stream_init_one(stream_s *stream) {
if (device_open(stream->dev) < 0) {
goto error;
}
if (stream->enc->type == ENCODER_TYPE_M2M || (RUN(h264) && !is_jpeg(stream->dev->run->format))) {
if (
stream->enc->type == ENCODER_TYPE_M2M_MJPEG
|| stream->enc->type == ENCODER_TYPE_M2M_JPEG
|| (RUN(h264) && !is_jpeg(stream->dev->run->format))
) {
device_export_to_dma(stream->dev);
}
if (device_switch_capturing(stream->dev, true) < 0) {

View File

@@ -81,6 +81,7 @@ typedef struct {
memsink_s *h264_sink;
unsigned h264_bitrate;
unsigned h264_gop;
char *h264_m2m_path;
stream_runtime_s *run;
} stream_s;