mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-11 01:53:43 +00:00
supported /dev/video21
This commit is contained in:
@@ -3,14 +3,14 @@
|
|||||||
.TH USTREAMER 1 "version 4.13" "November 2020"
|
.TH USTREAMER 1 "version 4.13" "November 2020"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer \- stream MJPG video from any V4L2 device to the network
|
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B ustreamer
|
.B ustreamer
|
||||||
.RI [OPTIONS]
|
.RI [OPTIONS]
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the Pi-KVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
|
µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the Pi-KVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
|
||||||
|
|
||||||
.SH USAGE
|
.SH USAGE
|
||||||
Without arguments, \fBustreamer\fR will try to open \fB/dev/video0\fR with 640x480 resolution and start streaming on \fBhttp://127\.0\.0\.1:8080\fR\. You can override this behavior using parameters \fB\-\-device\fR, \fB\-\-host\fR and \fB\-\-port\fR\. For example, to stream to the world, run: \fBustreamer --device=/dev/video1 --host=0.0.0.0 --port=80\fR
|
Without arguments, \fBustreamer\fR will try to open \fB/dev/video0\fR with 640x480 resolution and start streaming on \fBhttp://127\.0\.0\.1:8080\fR\. You can override this behavior using parameters \fB\-\-device\fR, \fB\-\-host\fR and \fB\-\-port\fR\. For example, to stream to the world, run: \fBustreamer --device=/dev/video1 --host=0.0.0.0 --port=80\fR
|
||||||
@@ -23,7 +23,7 @@ For example, the recommended way of running µStreamer with Auvidea B101 on a Ra
|
|||||||
.RS
|
.RS
|
||||||
\fB\-\-format=uyvy \e\fR # Device input format
|
\fB\-\-format=uyvy \e\fR # Device input format
|
||||||
.nf
|
.nf
|
||||||
\fB\-\-encoder=v4l2 \e\fR # Hardware encoding with V4L2 M2M intraface
|
\fB\-\-encoder=m2m-jpeg \e\fR # Hardware encoding with V4L2 M2M intraface
|
||||||
.nf
|
.nf
|
||||||
\fB\-\-workers=3 \e\fR # Maximum workers for V4L2 encoder
|
\fB\-\-workers=3 \e\fR # Maximum workers for V4L2 encoder
|
||||||
.nf
|
.nf
|
||||||
@@ -84,18 +84,20 @@ Default: 1 (the number of CPU cores (but not more than 4)).
|
|||||||
.TP
|
.TP
|
||||||
.BR \-q\ \fIN ", " \-\-quality\ \fIN
|
.BR \-q\ \fIN ", " \-\-quality\ \fIN
|
||||||
Set quality of JPEG encoding from 1 to 100 (best). Default: 80.
|
Set quality of JPEG encoding from 1 to 100 (best). Default: 80.
|
||||||
Note: If HW encoding is used (JPEG source format selected), this parameter attempts to configure the camera or capture device hardware's internal encoder. It does not re\-encode MJPG to MJPG to change the quality level for sources that already output MJPG.
|
Note: If HW encoding is used (JPEG source format selected), this parameter attempts to configure the camera or capture device hardware's internal encoder. It does not re\-encode MJPEG to MJPEG to change the quality level for sources that already output MJPEG.
|
||||||
.TP
|
.TP
|
||||||
.BR \-c\ \fItype ", " \-\-encoder\ \fItype
|
.BR \-c\ \fItype ", " \-\-encoder\ \fItype
|
||||||
Use specified encoder. It may affect the number of workers.
|
Use specified encoder. It may affect the number of workers.
|
||||||
|
|
||||||
CPU ─ Software MJPG encoding (default).
|
CPU ─ Software MJPEG encoding (default).
|
||||||
|
|
||||||
HW ─ Use pre-encoded MJPG frames directly from camera hardware.
|
HW ─ Use pre-encoded MJPEG frames directly from camera hardware.
|
||||||
|
|
||||||
V4L2 ─ GPU-accelerated MJPG encoding.
|
M2M-MJPEG ─ GPU-accelerated MJPEG encoding.
|
||||||
|
|
||||||
NOOP ─ Don't compress MJPG stream (do nothing).
|
M2M-JPEG ─ GPU-accelerated JPEG encoding.
|
||||||
|
|
||||||
|
NOOP ─ Don't compress MJPEG stream (do nothing).
|
||||||
.TP
|
.TP
|
||||||
.BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,...
|
.BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,...
|
||||||
It doesn't do anything. Still here for compatibility.
|
It doesn't do anything. Still here for compatibility.
|
||||||
@@ -114,6 +116,9 @@ Timeout for device querying. Default: 1.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-\-device\-error\-delay\ \fIsec
|
.BR \-\-device\-error\-delay\ \fIsec
|
||||||
Delay before trying to connect to the device again after an error (timeout for example). Default: 1.
|
Delay before trying to connect to the device again after an error (timeout for example). Default: 1.
|
||||||
|
.TP
|
||||||
|
.BR \-\-m2m\-device\ \fI/dev/path
|
||||||
|
Path to V4L2 mem-to-mem encoder device. Default: auto-select.
|
||||||
|
|
||||||
.SS "Image control options"
|
.SS "Image control options"
|
||||||
.TP
|
.TP
|
||||||
@@ -221,9 +226,6 @@ Timeout for lock. Default: 1.
|
|||||||
|
|
||||||
.SS "H264 sink options"
|
.SS "H264 sink options"
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-h264\-device\ \fI/dev/path
|
|
||||||
Path to V4L2 mem-to-mem encoder device. Default: /dev/video11.
|
|
||||||
.TP
|
|
||||||
.BR \-\-h264\-sink\ \fIname
|
.BR \-\-h264\-sink\ \fIname
|
||||||
Use the specified shared memory object to sink H264 frames. Default: disabled.
|
Use the specified shared memory object to sink H264 frames. Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
@@ -244,6 +246,10 @@ H264 bitrate in Kbps. Default: 5000.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-\-h264\-gop\ \fIN
|
.BR \-\-h264\-gop\ \fIN
|
||||||
Intarval between keyframes. Default: 30.
|
Intarval between keyframes. Default: 30.
|
||||||
|
.TP
|
||||||
|
.BR \-\-h264\-m2m\-device\ \fI/dev/path
|
||||||
|
Path to V4L2 mem-to-mem encoder device. Default: auto-select.
|
||||||
|
|
||||||
|
|
||||||
.SS "Process options"
|
.SS "Process options"
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ static const struct {
|
|||||||
const char *name;
|
const char *name;
|
||||||
const encoder_type_e type;
|
const encoder_type_e type;
|
||||||
} _ENCODER_TYPES[] = {
|
} _ENCODER_TYPES[] = {
|
||||||
{"CPU", ENCODER_TYPE_CPU},
|
{"CPU", ENCODER_TYPE_CPU},
|
||||||
{"HW", ENCODER_TYPE_HW},
|
{"HW", ENCODER_TYPE_HW},
|
||||||
{"M2M", ENCODER_TYPE_M2M},
|
{"M2M-MJPEG", ENCODER_TYPE_M2M_MJPEG},
|
||||||
{"NOOP", ENCODER_TYPE_NOOP},
|
{"M2M-JPEG", ENCODER_TYPE_M2M_JPEG},
|
||||||
|
{"NOOP", ENCODER_TYPE_NOOP},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +54,6 @@ encoder_s *encoder_init(void) {
|
|||||||
A_CALLOC(enc, 1);
|
A_CALLOC(enc, 1);
|
||||||
enc->type = run->type;
|
enc->type = run->type;
|
||||||
enc->n_workers = get_cores_available();
|
enc->n_workers = get_cores_available();
|
||||||
enc->m2m_path = "/dev/video11";
|
|
||||||
enc->run = run;
|
enc->run = run;
|
||||||
return enc;
|
return enc;
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ void encoder_destroy(encoder_s *enc) {
|
|||||||
|
|
||||||
encoder_type_e encoder_parse_type(const char *str) {
|
encoder_type_e encoder_parse_type(const char *str) {
|
||||||
if (!strcasecmp(str, "OMX")) {
|
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) {
|
for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
|
||||||
if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
|
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);
|
quality = DR(jpeg_quality);
|
||||||
n_workers = 1;
|
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 ...");
|
LOG_DEBUG("Preparing M2M encoder ...");
|
||||||
if (ER(m2ms) == NULL) {
|
if (ER(m2ms) == NULL) {
|
||||||
A_CALLOC(ER(m2ms), n_workers);
|
A_CALLOC(ER(m2ms), n_workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
|
||||||
if (ER(n_m2ms) < n_workers) {
|
if (ER(n_m2ms) < n_workers) {
|
||||||
double b_min = ENCODER_M2M_BITRATE_MIN;
|
if (type == ENCODER_TYPE_M2M_MJPEG) {
|
||||||
double b_max = ENCODER_M2M_BITRATE_MAX;
|
double b_min = ENCODER_M2M_BITRATE_MIN;
|
||||||
double step = ENCODER_M2M_BITRATE_STEP;
|
double b_max = ENCODER_M2M_BITRATE_MAX;
|
||||||
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
|
double step = ENCODER_M2M_BITRATE_STEP;
|
||||||
bitrate = step * round(bitrate / step);
|
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
|
||||||
LOG_VERBOSE("Using JPEG M2M bitrate: %u%% -> %u Kbps", quality, (unsigned)bitrate);
|
bitrate = step * round(bitrate / step);
|
||||||
|
|
||||||
// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
|
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
|
||||||
for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
|
assert(bitrate > 0);
|
||||||
assert(bitrate > 0);
|
char name[32];
|
||||||
char name[32];
|
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
|
||||||
snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
|
m2m_option_s options[] = {
|
||||||
m2m_option_s options[] = {
|
{"BITRATE", true, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate * 1000},
|
||||||
{"BITRATE", true, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate * 1000},
|
{NULL, false, 0, 0},
|
||||||
{NULL, false, 0, 0},
|
};
|
||||||
};
|
ER(m2ms[ER(n_m2ms)]) = m2m_encoder_init(name, enc->m2m_path, V4L2_PIX_FMT_MJPEG, 0, options);
|
||||||
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)");
|
LOG_VERBOSE("Compressing buffer using HW (just copying)");
|
||||||
hw_encoder_compress(src, dest);
|
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");
|
LOG_VERBOSE("Compressing buffer using M2M");
|
||||||
if (m2m_encoder_ensure_ready(ER(m2ms[wr->number]), src) < 0) {
|
if (m2m_encoder_ensure_ready(ER(m2ms[wr->number]), src) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
|
|||||||
@@ -60,13 +60,14 @@
|
|||||||
#define ENCODER_M2M_BITRATE_STEP ((unsigned)CFG_ENCODER_M2M_BITRATE_STEP)
|
#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 {
|
typedef enum {
|
||||||
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
|
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
|
||||||
ENCODER_TYPE_CPU,
|
ENCODER_TYPE_CPU,
|
||||||
ENCODER_TYPE_HW,
|
ENCODER_TYPE_HW,
|
||||||
ENCODER_TYPE_M2M,
|
ENCODER_TYPE_M2M_MJPEG,
|
||||||
|
ENCODER_TYPE_M2M_JPEG,
|
||||||
ENCODER_TYPE_NOOP,
|
ENCODER_TYPE_NOOP,
|
||||||
} encoder_type_e;
|
} encoder_type_e;
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
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);
|
LOG_INFO("%s: Initializing encoder ...", name);
|
||||||
|
|
||||||
m2m_encoder_s *enc;
|
m2m_encoder_s *enc;
|
||||||
A_CALLOC(enc, 1);
|
A_CALLOC(enc, 1);
|
||||||
assert(enc->name = strdup(name));
|
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->output_format = format;
|
||||||
enc->fps = fps;
|
enc->fps = fps;
|
||||||
enc->last_online = -1;
|
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.field = V4L2_FIELD_ANY;
|
||||||
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
|
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
|
||||||
fmt.fmt.pix_mp.num_planes = 1;
|
fmt.fmt.pix_mp.num_planes = 1;
|
||||||
fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
|
//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].sizeimage = 512 << 10;
|
||||||
E_LOG_DEBUG("Configuring OUTPUT format ...");
|
E_LOG_DEBUG("Configuring OUTPUT format ...");
|
||||||
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
|
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
|
||||||
if (fmt.fmt.pix_mp.pixelformat != enc->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));
|
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) {
|
if (_m2m_encoder_compress_raw(enc, src, dest, force_key) < 0) {
|
||||||
_m2m_encoder_cleanup(enc);
|
_m2m_encoder_cleanup(enc);
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ enum _OPT_VALUES {
|
|||||||
ADD_SINK(H264_SINK)
|
ADD_SINK(H264_SINK)
|
||||||
_O_H264_BITRATE,
|
_O_H264_BITRATE,
|
||||||
_O_H264_GOP,
|
_O_H264_GOP,
|
||||||
|
_O_H264_M2M_DEVICE,
|
||||||
# undef ADD_SINK
|
# undef ADD_SINK
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
@@ -191,6 +192,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
ADD_SINK("h264-", H264_SINK)
|
ADD_SINK("h264-", H264_SINK)
|
||||||
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
|
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
|
||||||
{"h264-gop", required_argument, NULL, _O_H264_GOP},
|
{"h264-gop", required_argument, NULL, _O_H264_GOP},
|
||||||
|
{"h264-m2m-device", required_argument, NULL, _O_H264_M2M_DEVICE},
|
||||||
# undef ADD_SINK
|
# undef ADD_SINK
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# 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("", sink, SINK)
|
||||||
ADD_SINK("raw-", raw_sink, RAW_SINK)
|
ADD_SINK("raw-", raw_sink, RAW_SINK)
|
||||||
ADD_SINK("h264-", h264_sink, H264_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_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_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
|
# undef ADD_SINK
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# 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) {
|
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__)
|
# 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("═══════════════════════════════════════════════════");
|
||||||
SAY("Version: %s; license: GPLv3", VERSION);
|
SAY("Version: %s; license: GPLv3", VERSION);
|
||||||
SAY("Copyright (C) 2018-2021 Maxim Devaev <mdevaev@gmail.com>\n");
|
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(" Note: If HW encoding is used (JPEG source format selected),");
|
||||||
SAY(" this parameter attempts to configure the camera");
|
SAY(" this parameter attempts to configure the camera");
|
||||||
SAY(" or capture device hardware's internal encoder.");
|
SAY(" or capture device hardware's internal encoder.");
|
||||||
SAY(" It does not re-encode MJPG to MJPG to change the quality level");
|
SAY(" It does not re-encode MJPEG to MJPEG to change the quality level");
|
||||||
SAY(" for sources that already output MJPG.\n");
|
SAY(" for sources that already output MJPEG.\n");
|
||||||
SAY(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.");
|
SAY(" -c|--encoder <type> ───────────────── Use specified encoder. It may affect the number of workers.");
|
||||||
SAY(" Available:");
|
SAY(" Available:");
|
||||||
SAY(" * CPU ── Software MJPG encoding (default);");
|
SAY(" * CPU ──────── Software MJPEG encoding (default);");
|
||||||
SAY(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware;");
|
SAY(" * HW ───────── Use pre-encoded MJPEG frames directly from camera hardware;");
|
||||||
SAY(" * M2M ── GPU-accelerated MJPG encoding using V4L2 M2M interface;");
|
SAY(" * M2M-MJPEG ── GPU-accelerated MJPEG encoding using V4L2 M2M interface;");
|
||||||
SAY(" * NOOP ─ Don't compress MJPG stream (do nothing).\n");
|
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(" -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(" -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(" 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-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(" --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(" 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("Image control options:");
|
||||||
SAY("══════════════════════");
|
SAY("══════════════════════");
|
||||||
SAY(" --image-default ────────────────────── Reset all image settings below to default. Default: no change.\n");
|
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("JPEG", "")
|
||||||
ADD_SINK("RAW", "raw-")
|
ADD_SINK("RAW", "raw-")
|
||||||
ADD_SINK("H264", "h264-")
|
ADD_SINK("H264", "h264-")
|
||||||
SAY(" --h264-bitrate <kbps> ──────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
|
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-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
|
# undef ADD_SINK
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
SAY("GPIO options:");
|
SAY("GPIO options:");
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ void stream_loop(stream_s *stream) {
|
|||||||
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
|
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
|
||||||
|
|
||||||
if (stream->h264_sink) {
|
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;) {
|
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) {
|
if (device_open(stream->dev) < 0) {
|
||||||
goto error;
|
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);
|
device_export_to_dma(stream->dev);
|
||||||
}
|
}
|
||||||
if (device_switch_capturing(stream->dev, true) < 0) {
|
if (device_switch_capturing(stream->dev, true) < 0) {
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ typedef struct {
|
|||||||
memsink_s *h264_sink;
|
memsink_s *h264_sink;
|
||||||
unsigned h264_bitrate;
|
unsigned h264_bitrate;
|
||||||
unsigned h264_gop;
|
unsigned h264_gop;
|
||||||
|
char *h264_m2m_path;
|
||||||
|
|
||||||
stream_runtime_s *run;
|
stream_runtime_s *run;
|
||||||
} stream_s;
|
} stream_s;
|
||||||
|
|||||||
Reference in New Issue
Block a user