mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 12:16:31 +00:00
849 lines
38 KiB
C
849 lines
38 KiB
C
/*****************************************************************************
|
|
# #
|
|
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
|
|
# #
|
|
# Copyright (C) 2018-2024 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 "options.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
|
|
#include "../libs/types.h"
|
|
#include "../libs/const.h"
|
|
#include "../libs/tools.h"
|
|
#include "../libs/logging.h"
|
|
#include "../libs/process.h"
|
|
#include "../libs/frame.h"
|
|
#include "../libs/memsink.h"
|
|
#include "../libs/options.h"
|
|
#include "../libs/capture.h"
|
|
#ifdef WITH_V4P
|
|
# include "../libs/drm/drm.h"
|
|
#endif
|
|
|
|
#include "encoder.h"
|
|
#include "stream.h"
|
|
#include "http/server.h"
|
|
#ifdef WITH_GPIO
|
|
# include "gpio/gpio.h"
|
|
#endif
|
|
|
|
|
|
enum _US_OPT_VALUES {
|
|
_O_DEVICE = 'd',
|
|
_O_INPUT = 'i',
|
|
_O_RESOLUTION = 'r',
|
|
_O_FORMAT = 'm',
|
|
_O_TV_STANDARD = 'a',
|
|
_O_IO_METHOD = 'I',
|
|
_O_DESIRED_FPS = 'f',
|
|
_O_MIN_FRAME_SIZE = 'z',
|
|
_O_ALLOW_TRUNCATED_FRAMES = 'T',
|
|
_O_PERSISTENT = 'n',
|
|
_O_DV_TIMINGS = 't',
|
|
_O_BUFFERS = 'b',
|
|
_O_WORKERS = 'w',
|
|
_O_QUALITY = 'q',
|
|
_O_ENCODER = 'c',
|
|
_O_GLITCHED_RESOLUTIONS = 'g', // Deprecated
|
|
_O_BLANK = 'k',
|
|
_O_LAST_AS_BLANK = 'K',
|
|
_O_SLOWDOWN = 'l',
|
|
|
|
_O_HOST = 's',
|
|
_O_PORT = 'p',
|
|
_O_UNIX = 'U',
|
|
_O_UNIX_RM = 'D',
|
|
_O_UNIX_MODE = 'M',
|
|
# ifdef WITH_SYSTEMD
|
|
_O_SYSTEMD = 'S',
|
|
# endif
|
|
_O_DROP_SAME_FRAMES = 'e',
|
|
_O_FAKE_RESOLUTION = 'R',
|
|
|
|
_O_HELP = 'h',
|
|
_O_VERSION = 'v',
|
|
|
|
// Longs only
|
|
|
|
_O_DEVICE_TIMEOUT = 10000,
|
|
_O_DEVICE_ERROR_DELAY,
|
|
_O_FORMAT_SWAP_RGB,
|
|
_O_M2M_DEVICE,
|
|
|
|
_O_IMAGE_DEFAULT,
|
|
_O_BRIGHTNESS,
|
|
_O_CONTRAST,
|
|
_O_SATURATION,
|
|
_O_HUE,
|
|
_O_GAMMA,
|
|
_O_SHARPNESS,
|
|
_O_BACKLIGHT_COMPENSATION,
|
|
_O_WHITE_BALANCE,
|
|
_O_GAIN,
|
|
_O_COLOR_EFFECT,
|
|
_O_ROTATE,
|
|
_O_FLIP_VERTICAL,
|
|
_O_FLIP_HORIZONTAL,
|
|
|
|
_O_USER,
|
|
_O_PASSWD,
|
|
_O_STATIC,
|
|
_O_ALLOW_ORIGIN,
|
|
_O_INSTANCE_ID,
|
|
_O_TCP_NODELAY,
|
|
_O_SERVER_TIMEOUT,
|
|
|
|
# define ADD_SINK(x_prefix) \
|
|
_O_##x_prefix, \
|
|
_O_##x_prefix##_MODE, \
|
|
_O_##x_prefix##_RM, \
|
|
_O_##x_prefix##_CLIENT_TTL, \
|
|
_O_##x_prefix##_TIMEOUT,
|
|
ADD_SINK(JPEG_SINK)
|
|
ADD_SINK(RAW_SINK)
|
|
ADD_SINK(H264_SINK)
|
|
_O_H264_BITRATE,
|
|
_O_H264_GOP,
|
|
_O_H264_M2M_DEVICE,
|
|
_O_H264_BOOST,
|
|
# undef ADD_SINK
|
|
|
|
# ifdef WITH_V4P
|
|
_O_V4P,
|
|
# endif
|
|
|
|
# ifdef WITH_GPIO
|
|
_O_GPIO_DEVICE,
|
|
_O_GPIO_CONSUMER_PREFIX,
|
|
_O_GPIO_PROG_RUNNING,
|
|
_O_GPIO_STREAM_ONLINE,
|
|
_O_GPIO_HAS_HTTP_CLIENTS,
|
|
# endif
|
|
|
|
# ifdef WITH_PDEATHSIG
|
|
_O_EXIT_ON_PARENT_DEATH,
|
|
# endif
|
|
_O_EXIT_ON_DEVICE_ERROR,
|
|
_O_EXIT_ON_NO_CLIENTS,
|
|
# ifdef WITH_SETPROCTITLE
|
|
_O_PROCESS_NAME_PREFIX,
|
|
# endif
|
|
_O_NOTIFY_PARENT,
|
|
|
|
_O_LOG_LEVEL,
|
|
_O_PERF,
|
|
_O_VERBOSE,
|
|
_O_DEBUG,
|
|
_O_FORCE_LOG_COLORS,
|
|
_O_NO_LOG_COLORS,
|
|
|
|
_O_FEATURES,
|
|
};
|
|
|
|
static const struct option _LONG_OPTS[] = {
|
|
{"device", required_argument, NULL, _O_DEVICE},
|
|
{"input", required_argument, NULL, _O_INPUT},
|
|
{"resolution", required_argument, NULL, _O_RESOLUTION},
|
|
{"format", required_argument, NULL, _O_FORMAT},
|
|
{"format-swap-rgb", no_argument, NULL, _O_FORMAT_SWAP_RGB},
|
|
{"tv-standard", required_argument, NULL, _O_TV_STANDARD},
|
|
{"io-method", required_argument, NULL, _O_IO_METHOD},
|
|
{"desired-fps", required_argument, NULL, _O_DESIRED_FPS},
|
|
{"min-frame-size", required_argument, NULL, _O_MIN_FRAME_SIZE},
|
|
{"allow-truncated-frames", no_argument, NULL, _O_ALLOW_TRUNCATED_FRAMES},
|
|
{"persistent", no_argument, NULL, _O_PERSISTENT},
|
|
{"dv-timings", no_argument, NULL, _O_DV_TIMINGS},
|
|
{"buffers", required_argument, NULL, _O_BUFFERS},
|
|
{"workers", required_argument, NULL, _O_WORKERS},
|
|
{"quality", required_argument, NULL, _O_QUALITY},
|
|
{"encoder", required_argument, NULL, _O_ENCODER},
|
|
{"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},
|
|
{"device-timeout", required_argument, NULL, _O_DEVICE_TIMEOUT},
|
|
{"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY},
|
|
{"m2m-device", required_argument, NULL, _O_M2M_DEVICE},
|
|
|
|
{"image-default", no_argument, NULL, _O_IMAGE_DEFAULT},
|
|
{"brightness", required_argument, NULL, _O_BRIGHTNESS},
|
|
{"contrast", required_argument, NULL, _O_CONTRAST},
|
|
{"saturation", required_argument, NULL, _O_SATURATION},
|
|
{"hue", required_argument, NULL, _O_HUE},
|
|
{"gamma", required_argument, NULL, _O_GAMMA},
|
|
{"sharpness", required_argument, NULL, _O_SHARPNESS},
|
|
{"backlight-compensation", required_argument, NULL, _O_BACKLIGHT_COMPENSATION},
|
|
{"white-balance", required_argument, NULL, _O_WHITE_BALANCE},
|
|
{"gain", required_argument, NULL, _O_GAIN},
|
|
{"color-effect", required_argument, NULL, _O_COLOR_EFFECT},
|
|
{"rotate", required_argument, NULL, _O_ROTATE},
|
|
{"flip-vertical", required_argument, NULL, _O_FLIP_VERTICAL},
|
|
{"flip-horizontal", required_argument, NULL, _O_FLIP_HORIZONTAL},
|
|
|
|
{"host", required_argument, NULL, _O_HOST},
|
|
{"port", required_argument, NULL, _O_PORT},
|
|
{"unix", required_argument, NULL, _O_UNIX},
|
|
{"unix-rm", no_argument, NULL, _O_UNIX_RM},
|
|
{"unix-mode", required_argument, NULL, _O_UNIX_MODE},
|
|
# ifdef WITH_SYSTEMD
|
|
{"systemd", no_argument, NULL, _O_SYSTEMD},
|
|
# endif
|
|
{"user", required_argument, NULL, _O_USER},
|
|
{"passwd", required_argument, NULL, _O_PASSWD},
|
|
{"static", required_argument, NULL, _O_STATIC},
|
|
{"drop-same-frames", required_argument, NULL, _O_DROP_SAME_FRAMES},
|
|
{"allow-origin", required_argument, NULL, _O_ALLOW_ORIGIN},
|
|
{"instance-id", required_argument, NULL, _O_INSTANCE_ID},
|
|
{"fake-resolution", required_argument, NULL, _O_FAKE_RESOLUTION},
|
|
{"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY},
|
|
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
|
|
|
# define ADD_SINK(x_opt, x_prefix) \
|
|
{x_opt "-sink", required_argument, NULL, _O_##x_prefix}, \
|
|
{x_opt "-sink-mode", required_argument, NULL, _O_##x_prefix##_MODE}, \
|
|
{x_opt "-sink-rm", no_argument, NULL, _O_##x_prefix##_RM}, \
|
|
{x_opt "-sink-client-ttl", required_argument, NULL, _O_##x_prefix##_CLIENT_TTL}, \
|
|
{x_opt "-sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
|
ADD_SINK("jpeg", JPEG_SINK)
|
|
ADD_SINK("raw", RAW_SINK)
|
|
ADD_SINK("h264", H264_SINK)
|
|
# undef ADD_SINK
|
|
// Extra opts for H.264
|
|
{"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},
|
|
{"h264-boost", no_argument, NULL, _O_H264_BOOST},
|
|
// Compatibility
|
|
{"sink", required_argument, NULL, _O_JPEG_SINK},
|
|
{"sink-mode", required_argument, NULL, _O_JPEG_SINK_MODE},
|
|
{"sink-rm", no_argument, NULL, _O_JPEG_SINK_RM},
|
|
{"sink-client-ttl", required_argument, NULL, _O_JPEG_SINK_CLIENT_TTL},
|
|
{"sink-timeout", required_argument, NULL, _O_JPEG_SINK_TIMEOUT},
|
|
|
|
# ifdef WITH_V4P
|
|
{"v4p", no_argument, NULL, _O_V4P},
|
|
# endif
|
|
|
|
# ifdef WITH_GPIO
|
|
{"gpio-device", required_argument, NULL, _O_GPIO_DEVICE},
|
|
{"gpio-consumer-prefix", required_argument, NULL, _O_GPIO_CONSUMER_PREFIX},
|
|
{"gpio-prog-running", required_argument, NULL, _O_GPIO_PROG_RUNNING},
|
|
{"gpio-stream-online", required_argument, NULL, _O_GPIO_STREAM_ONLINE},
|
|
{"gpio-has-http-clients", required_argument, NULL, _O_GPIO_HAS_HTTP_CLIENTS},
|
|
# endif
|
|
|
|
# ifdef WITH_PDEATHSIG
|
|
{"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH},
|
|
# endif
|
|
{"exit-on-device-error", no_argument, NULL, _O_EXIT_ON_DEVICE_ERROR},
|
|
{"exit-on-no-clients", required_argument, NULL, _O_EXIT_ON_NO_CLIENTS},
|
|
# ifdef WITH_SETPROCTITLE
|
|
{"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX},
|
|
# endif
|
|
{"notify-parent", no_argument, NULL, _O_NOTIFY_PARENT},
|
|
|
|
{"log-level", required_argument, NULL, _O_LOG_LEVEL},
|
|
{"perf", no_argument, NULL, _O_PERF},
|
|
{"verbose", no_argument, NULL, _O_VERBOSE},
|
|
{"debug", no_argument, NULL, _O_DEBUG},
|
|
{"force-log-colors", no_argument, NULL, _O_FORCE_LOG_COLORS},
|
|
{"no-log-colors", no_argument, NULL, _O_NO_LOG_COLORS},
|
|
|
|
{"help", no_argument, NULL, _O_HELP},
|
|
{"version", no_argument, NULL, _O_VERSION},
|
|
{"features", no_argument, NULL, _O_FEATURES},
|
|
|
|
{NULL, 0, NULL, 0},
|
|
};
|
|
|
|
|
|
static int _parse_resolution(const char *str, uint *width, uint *height, bool limited);
|
|
static int _check_instance_id(const char *str);
|
|
|
|
static void _features(void);
|
|
|
|
static void _help(
|
|
FILE *fp,
|
|
const us_capture_s *cap,
|
|
const us_encoder_s *enc,
|
|
const us_stream_s *stream,
|
|
const us_server_s *server);
|
|
|
|
|
|
us_options_s *us_options_init(uint argc, char *argv[]) {
|
|
us_options_s *opts;
|
|
US_CALLOC(opts, 1);
|
|
opts->argc = argc;
|
|
opts->argv = argv;
|
|
|
|
US_CALLOC(opts->argv_copy, argc + 1);
|
|
for (uint i = 0; i < argc; ++i) {
|
|
opts->argv_copy[i] = us_strdup(argv[i]);
|
|
}
|
|
opts->argv_copy[argc] = NULL;
|
|
return opts;
|
|
}
|
|
|
|
void us_options_destroy(us_options_s *opts) {
|
|
US_DELETE(opts->jpeg_sink, us_memsink_destroy);
|
|
US_DELETE(opts->raw_sink, us_memsink_destroy);
|
|
US_DELETE(opts->h264_sink, us_memsink_destroy);
|
|
# ifdef WITH_V4P
|
|
US_DELETE(opts->drm, us_drm_destroy);
|
|
# endif
|
|
|
|
for (uint i = 0; i < opts->argc; ++i) {
|
|
free(opts->argv_copy[i]);
|
|
}
|
|
free(opts->argv_copy);
|
|
|
|
free(opts);
|
|
}
|
|
|
|
|
|
int us_options_parse(
|
|
us_options_s *opts,
|
|
us_capture_s *cap,
|
|
us_encoder_s *enc,
|
|
us_stream_s *stream,
|
|
us_server_s *server
|
|
) {
|
|
# define OPT_SET(x_dest, x_value) { \
|
|
x_dest = x_value; \
|
|
break; \
|
|
}
|
|
|
|
# define OPT_NUMBER(x_name, x_dest, x_min, x_max, x_base) { \
|
|
errno = 0; char *m_end = NULL; const sll m_tmp = strtoll(optarg, &m_end, x_base); \
|
|
if (errno || *m_end || m_tmp < x_min || m_tmp > x_max) { \
|
|
printf("Invalid value for '%s=%s': min=%lld, max=%lld\n", x_name, optarg, (sll)x_min, (sll)x_max); \
|
|
return -1; \
|
|
} \
|
|
x_dest = m_tmp; \
|
|
break; \
|
|
}
|
|
|
|
# define OPT_RESOLUTION(x_name, x_dest_width, x_dest_height, x_limited) { \
|
|
switch (_parse_resolution(optarg, &x_dest_width, &x_dest_height, x_limited)) { \
|
|
case -1: \
|
|
printf("Invalid resolution format for '%s=%s'\n", x_name, optarg); \
|
|
return -1; \
|
|
case -2: \
|
|
printf("Invalid width of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_WIDTH, US_VIDEO_MAX_WIDTH); \
|
|
return -1; \
|
|
case -3: \
|
|
printf("Invalid height of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_HEIGHT, US_VIDEO_MAX_HEIGHT); \
|
|
return -1; \
|
|
case 0: break; \
|
|
default: US_RAISE("Unknown error"); \
|
|
} \
|
|
break; \
|
|
}
|
|
|
|
# define OPT_PARSE_ENUM(x_name, x_dest, x_func, x_available) { \
|
|
const int m_value = x_func(optarg); \
|
|
if (m_value < 0) { \
|
|
printf("Unknown " x_name ": %s; available: %s\n", optarg, x_available); \
|
|
return -1; \
|
|
} \
|
|
x_dest = m_value; \
|
|
break; \
|
|
}
|
|
|
|
# define OPT_CTL_DEFAULT_NOBREAK(x_dest) { \
|
|
cap->ctl.x_dest.mode = CTL_MODE_DEFAULT; \
|
|
}
|
|
|
|
# define OPT_CTL_MANUAL(x_dest) { \
|
|
if (!strcasecmp(optarg, "default")) { \
|
|
OPT_CTL_DEFAULT_NOBREAK(x_dest); \
|
|
} else { \
|
|
cap->ctl.x_dest.mode = CTL_MODE_VALUE; \
|
|
OPT_NUMBER("--"#x_dest, cap->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
|
|
} \
|
|
break; \
|
|
}
|
|
|
|
# define OPT_CTL_AUTO(x_dest) { \
|
|
if (!strcasecmp(optarg, "default")) { \
|
|
OPT_CTL_DEFAULT_NOBREAK(x_dest); \
|
|
} else if (!strcasecmp(optarg, "auto")) { \
|
|
cap->ctl.x_dest.mode = CTL_MODE_AUTO; \
|
|
} else { \
|
|
cap->ctl.x_dest.mode = CTL_MODE_VALUE; \
|
|
OPT_NUMBER("--"#x_dest, cap->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
|
|
} \
|
|
break; \
|
|
}
|
|
|
|
# define ADD_SINK(x_prefix) \
|
|
const char *x_prefix##_name = NULL; \
|
|
mode_t x_prefix##_mode = 0660; \
|
|
bool x_prefix##_rm = false; \
|
|
uint x_prefix##_client_ttl = 10; \
|
|
uint x_prefix##_timeout = 1;
|
|
ADD_SINK(jpeg_sink);
|
|
ADD_SINK(raw_sink);
|
|
ADD_SINK(h264_sink);
|
|
# undef ADD_SINK
|
|
|
|
# ifdef WITH_SETPROCTITLE
|
|
const char *process_name_prefix = NULL;
|
|
# endif
|
|
|
|
char short_opts[128];
|
|
us_build_short_options(_LONG_OPTS, short_opts, 128);
|
|
|
|
for (int ch; (ch = getopt_long(opts->argc, opts->argv_copy, short_opts, _LONG_OPTS, NULL)) >= 0;) {
|
|
switch (ch) {
|
|
case _O_DEVICE: OPT_SET(cap->path, optarg);
|
|
case _O_INPUT: OPT_NUMBER("--input", cap->input, 0, 128, 0);
|
|
case _O_RESOLUTION: OPT_RESOLUTION("--resolution", cap->width, cap->height, true);
|
|
# pragma GCC diagnostic ignored "-Wsign-compare"
|
|
# pragma GCC diagnostic push
|
|
case _O_FORMAT: OPT_PARSE_ENUM("pixel format", cap->format, us_capture_parse_format, US_FORMATS_STR);
|
|
# pragma GCC diagnostic pop
|
|
case _O_FORMAT_SWAP_RGB: OPT_SET(cap->format_swap_rgb, true);
|
|
case _O_TV_STANDARD: OPT_PARSE_ENUM("TV standard", cap->standard, us_capture_parse_standard, US_STANDARDS_STR);
|
|
case _O_IO_METHOD: OPT_PARSE_ENUM("IO method", cap->io_method, us_capture_parse_io_method, US_IO_METHODS_STR);
|
|
case _O_DESIRED_FPS: OPT_NUMBER("--desired-fps", stream->desired_fps, 0, US_VIDEO_MAX_FPS, 0);
|
|
case _O_MIN_FRAME_SIZE: OPT_NUMBER("--min-frame-size", cap->min_frame_size, 1, 8192, 0);
|
|
case _O_ALLOW_TRUNCATED_FRAMES: OPT_SET(cap->allow_truncated_frames, true);
|
|
case _O_PERSISTENT: OPT_SET(cap->persistent, true);
|
|
case _O_DV_TIMINGS: OPT_SET(cap->dv_timings, true);
|
|
case _O_BUFFERS: OPT_NUMBER("--buffers", cap->n_bufs, 1, 32, 0);
|
|
case _O_WORKERS: OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
|
|
case _O_QUALITY: OPT_NUMBER("--quality", cap->jpeg_quality, 1, 100, 0);
|
|
case _O_ENCODER: OPT_PARSE_ENUM("encoder type", enc->type, us_encoder_parse_type, ENCODER_TYPES_STR);
|
|
case _O_GLITCHED_RESOLUTIONS: break; // Deprecated
|
|
case _O_BLANK: break; // Deprecated
|
|
case _O_LAST_AS_BLANK: break; // Deprecated
|
|
case _O_SLOWDOWN: OPT_SET(stream->slowdown, true);
|
|
case _O_DEVICE_TIMEOUT: OPT_NUMBER("--device-timeout", cap->timeout, 1, 60, 0);
|
|
case _O_DEVICE_ERROR_DELAY: OPT_NUMBER("--device-error-delay", stream->error_delay, 1, 60, 0);
|
|
case _O_M2M_DEVICE: OPT_SET(enc->m2m_path, optarg);
|
|
|
|
case _O_IMAGE_DEFAULT:
|
|
OPT_CTL_DEFAULT_NOBREAK(brightness);
|
|
OPT_CTL_DEFAULT_NOBREAK(contrast);
|
|
OPT_CTL_DEFAULT_NOBREAK(saturation);
|
|
OPT_CTL_DEFAULT_NOBREAK(hue);
|
|
OPT_CTL_DEFAULT_NOBREAK(gamma);
|
|
OPT_CTL_DEFAULT_NOBREAK(sharpness);
|
|
OPT_CTL_DEFAULT_NOBREAK(backlight_compensation);
|
|
OPT_CTL_DEFAULT_NOBREAK(white_balance);
|
|
OPT_CTL_DEFAULT_NOBREAK(gain);
|
|
OPT_CTL_DEFAULT_NOBREAK(color_effect);
|
|
OPT_CTL_DEFAULT_NOBREAK(rotate);
|
|
OPT_CTL_DEFAULT_NOBREAK(flip_vertical);
|
|
OPT_CTL_DEFAULT_NOBREAK(flip_horizontal);
|
|
break;
|
|
case _O_BRIGHTNESS: OPT_CTL_AUTO(brightness);
|
|
case _O_CONTRAST: OPT_CTL_MANUAL(contrast);
|
|
case _O_SATURATION: OPT_CTL_MANUAL(saturation);
|
|
case _O_HUE: OPT_CTL_AUTO(hue);
|
|
case _O_GAMMA: OPT_CTL_MANUAL(gamma);
|
|
case _O_SHARPNESS: OPT_CTL_MANUAL(sharpness);
|
|
case _O_BACKLIGHT_COMPENSATION: OPT_CTL_MANUAL(backlight_compensation);
|
|
case _O_WHITE_BALANCE: OPT_CTL_AUTO(white_balance);
|
|
case _O_GAIN: OPT_CTL_AUTO(gain);
|
|
case _O_COLOR_EFFECT: OPT_CTL_MANUAL(color_effect);
|
|
case _O_ROTATE: OPT_CTL_MANUAL(rotate);
|
|
case _O_FLIP_VERTICAL: OPT_CTL_MANUAL(flip_vertical);
|
|
case _O_FLIP_HORIZONTAL: OPT_CTL_MANUAL(flip_horizontal);
|
|
|
|
case _O_HOST: OPT_SET(server->host, optarg);
|
|
case _O_PORT: OPT_NUMBER("--port", server->port, 1, 65535, 0);
|
|
case _O_UNIX: OPT_SET(server->unix_path, optarg);
|
|
case _O_UNIX_RM: OPT_SET(server->unix_rm, true);
|
|
case _O_UNIX_MODE: OPT_NUMBER("--unix-mode", server->unix_mode, INT_MIN, INT_MAX, 8);
|
|
# ifdef WITH_SYSTEMD
|
|
case _O_SYSTEMD: OPT_SET(server->systemd, true);
|
|
# endif
|
|
case _O_USER: OPT_SET(server->user, optarg);
|
|
case _O_PASSWD: OPT_SET(server->passwd, optarg);
|
|
case _O_STATIC: OPT_SET(server->static_path, optarg);
|
|
case _O_DROP_SAME_FRAMES: OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, US_VIDEO_MAX_FPS, 0);
|
|
case _O_FAKE_RESOLUTION: OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false);
|
|
case _O_ALLOW_ORIGIN: OPT_SET(server->allow_origin, optarg);
|
|
case _O_INSTANCE_ID:
|
|
if (_check_instance_id(optarg) != 0) {
|
|
printf("Invalid instance ID, it should be like: ^[a-zA-Z0-9\\./+_-]*$\n");
|
|
return -1;
|
|
}
|
|
server->instance_id = optarg;
|
|
break;
|
|
case _O_TCP_NODELAY: OPT_SET(server->tcp_nodelay, true);
|
|
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
|
|
|
|
# define ADD_SINK(x_opt, x_lp, x_up) \
|
|
case _O_##x_up: OPT_SET(x_lp##_name, optarg); \
|
|
case _O_##x_up##_MODE: OPT_NUMBER("--" #x_opt "-sink-mode", x_lp##_mode, INT_MIN, INT_MAX, 8); \
|
|
case _O_##x_up##_RM: OPT_SET(x_lp##_rm, true); \
|
|
case _O_##x_up##_CLIENT_TTL: OPT_NUMBER("--" #x_opt "-sink-client-ttl", x_lp##_client_ttl, 1, 60, 0); \
|
|
case _O_##x_up##_TIMEOUT: OPT_NUMBER("--" #x_opt "-sink-timeout", x_lp##_timeout, 1, 60, 0);
|
|
ADD_SINK("jpeg", jpeg_sink, JPEG_SINK)
|
|
ADD_SINK("raw", raw_sink, RAW_SINK)
|
|
ADD_SINK("h264", h264_sink, H264_SINK)
|
|
# undef ADD_SINK
|
|
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 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);
|
|
case _O_H264_BOOST: OPT_SET(stream->h264_boost, true);
|
|
|
|
# ifdef WITH_V4P
|
|
case _O_V4P:
|
|
opts->drm = us_drm_init();
|
|
stream->drm = opts->drm;
|
|
break;
|
|
# endif
|
|
|
|
# ifdef WITH_GPIO
|
|
case _O_GPIO_DEVICE: OPT_SET(us_g_gpio.path, optarg);
|
|
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(us_g_gpio.consumer_prefix, optarg);
|
|
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", us_g_gpio.prog_running.pin, 0, 256, 0);
|
|
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", us_g_gpio.stream_online.pin, 0, 256, 0);
|
|
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", us_g_gpio.has_http_clients.pin, 0, 256, 0);
|
|
# endif
|
|
|
|
# ifdef WITH_PDEATHSIG
|
|
case _O_EXIT_ON_PARENT_DEATH:
|
|
if (us_process_track_parent_death() < 0) {
|
|
return -1;
|
|
};
|
|
break;
|
|
# endif
|
|
case _O_EXIT_ON_DEVICE_ERROR: OPT_SET(stream->exit_on_device_error, true);
|
|
case _O_EXIT_ON_NO_CLIENTS: OPT_NUMBER("--exit-on-no-clients", stream->exit_on_no_clients, 0, 86400, 0);
|
|
# ifdef WITH_SETPROCTITLE
|
|
case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg);
|
|
# endif
|
|
case _O_NOTIFY_PARENT: OPT_SET(stream->notify_parent, true);
|
|
|
|
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
|
|
case _O_PERF: OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
|
|
case _O_VERBOSE: OPT_SET(us_g_log_level, US_LOG_LEVEL_VERBOSE);
|
|
case _O_DEBUG: OPT_SET(us_g_log_level, US_LOG_LEVEL_DEBUG);
|
|
case _O_FORCE_LOG_COLORS: OPT_SET(us_g_log_colored, true);
|
|
case _O_NO_LOG_COLORS: OPT_SET(us_g_log_colored, false);
|
|
|
|
case _O_HELP: _help(stdout, cap, enc, stream, server); return 1;
|
|
case _O_VERSION: puts(US_VERSION); return 1;
|
|
case _O_FEATURES: _features(); return 1;
|
|
|
|
case 0: break;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
US_LOG_INFO("Starting PiKVM uStreamer %s ...", US_VERSION);
|
|
|
|
# define ADD_SINK(x_label, x_prefix) { \
|
|
if (x_prefix##_name && x_prefix##_name[0] != '\0') { \
|
|
opts->x_prefix = us_memsink_init_opened( \
|
|
x_label, \
|
|
x_prefix##_name, \
|
|
true, \
|
|
x_prefix##_mode, \
|
|
x_prefix##_rm, \
|
|
x_prefix##_client_ttl, \
|
|
x_prefix##_timeout \
|
|
); \
|
|
} \
|
|
stream->x_prefix = opts->x_prefix; \
|
|
}
|
|
ADD_SINK("JPEG", jpeg_sink);
|
|
ADD_SINK("RAW", raw_sink);
|
|
ADD_SINK("H264", h264_sink);
|
|
# undef ADD_SINK
|
|
|
|
# ifdef WITH_SETPROCTITLE
|
|
if (process_name_prefix != NULL) {
|
|
us_process_set_name_prefix(opts->argc, opts->argv, process_name_prefix);
|
|
}
|
|
# endif
|
|
|
|
# undef OPT_CTL_AUTO
|
|
# undef OPT_CTL_MANUAL
|
|
# undef OPT_CTL_DEFAULT_NOBREAK
|
|
# undef OPT_PARSE
|
|
# undef OPT_RESOLUTION
|
|
# undef OPT_NUMBER
|
|
# undef OPT_SET
|
|
return 0;
|
|
}
|
|
|
|
static int _parse_resolution(const char *str, uint *width, uint *height, bool limited) {
|
|
uint tmp_width;
|
|
uint tmp_height;
|
|
if (sscanf(str, "%ux%u", &tmp_width, &tmp_height) != 2) {
|
|
return -1;
|
|
}
|
|
if (limited) {
|
|
if (tmp_width < US_VIDEO_MIN_WIDTH || tmp_width > US_VIDEO_MAX_WIDTH) {
|
|
return -2;
|
|
}
|
|
if (tmp_height < US_VIDEO_MIN_HEIGHT || tmp_height > US_VIDEO_MAX_HEIGHT) {
|
|
return -3;
|
|
}
|
|
}
|
|
*width = tmp_width;
|
|
*height = tmp_height;
|
|
return 0;
|
|
}
|
|
|
|
static int _check_instance_id(const char *str) {
|
|
for (const char *ptr = str; *ptr; ++ptr) {
|
|
if (!(isascii(*ptr) && (
|
|
isalpha(*ptr) || isdigit(*ptr)
|
|
|| *ptr == '.' || *ptr == '/' || *ptr == '+' || *ptr == '_' || *ptr == '-'
|
|
))) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void _features(void) {
|
|
# ifdef MK_WITH_PYTHON
|
|
puts("+ WITH_PYTHON");
|
|
# else
|
|
puts("- WITH_PYTHON");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_JANUS
|
|
puts("+ WITH_JANUS");
|
|
# else
|
|
puts("- WITH_JANUS");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_V4P
|
|
puts("+ WITH_V4P");
|
|
# else
|
|
puts("- WITH_V4P");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_GPIO
|
|
puts("+ WITH_GPIO");
|
|
# else
|
|
puts("- WITH_GPIO");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_SYSTEMD
|
|
puts("+ WITH_SYSTEMD");
|
|
# else
|
|
puts("- WITH_SYSTEMD");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_PTHREAD_NP
|
|
puts("+ WITH_PTHREAD_NP");
|
|
# else
|
|
puts("- WITH_PTHREAD_NP");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_SETPROCTITLE
|
|
puts("+ WITH_SETPROCTITLE");
|
|
# else
|
|
puts("- WITH_SETPROCTITLE");
|
|
# endif
|
|
|
|
# ifdef MK_WITH_PDEATHSIG
|
|
puts("+ WITH_PDEATHSIG");
|
|
# else
|
|
puts("- WITH_PDEATHSIG");
|
|
# endif
|
|
}
|
|
|
|
static void _help(
|
|
FILE *fp,
|
|
const us_capture_s *cap,
|
|
const us_encoder_s *enc,
|
|
const us_stream_s *stream,
|
|
const us_server_s *server
|
|
) {
|
|
# define SAY(x_msg, ...) fprintf(fp, x_msg "\n", ##__VA_ARGS__)
|
|
SAY("\nuStreamer - Lightweight and fast MJPEG-HTTP streamer");
|
|
SAY("═══════════════════════════════════════════════════");
|
|
SAY("Version: %s; license: GPLv3", US_VERSION);
|
|
SAY("Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com>\n");
|
|
SAY("Capturing options:");
|
|
SAY("══════════════════");
|
|
SAY(" -d|--device </dev/path> ───────────── Path to V4L2 device. Default: %s.\n", cap->path);
|
|
SAY(" -i|--input <N> ────────────────────── Input channel. Default: %u.\n", cap->input);
|
|
SAY(" -r|--resolution <WxH> ─────────────── Initial image resolution. Default: %ux%u.\n", cap->width, cap->height);
|
|
SAY(" -m|--format <fmt> ─────────────────── Image format.");
|
|
SAY(" Available: %s; default: YUYV.\n", US_FORMATS_STR);
|
|
SAY(" --format-swap-rgb ──────────────── Enable R-G-B order swapping: RGB to BGR and vice versa.");
|
|
SAY(" Default: disabled.\n");
|
|
SAY(" -a|--tv-standard <std> ────────────── Force TV standard.");
|
|
SAY(" Available: %s; default: disabled.\n", US_STANDARDS_STR);
|
|
SAY(" -I|--io-method <method> ───────────── Set V4L2 IO method (see kernel documentation).");
|
|
SAY(" Changing of this parameter may increase the performance. Or not.");
|
|
SAY(" Available: %s; default: MMAP.\n", US_IO_METHODS_STR);
|
|
SAY(" -f|--desired-fps <N> ──────────────── Desired FPS. Default: maximum possible.\n");
|
|
SAY(" -z|--min-frame-size <N> ───────────── Drop frames smaller than this limit. Useful if the device");
|
|
SAY(" produces small-sized garbage frames. Default: %zu bytes.\n", cap->min_frame_size);
|
|
SAY(" -T|--allow-truncated-frames ───────── Allows to handle truncated frames. Useful if the device");
|
|
SAY(" produces incorrect but still acceptable frames. Default: disabled.\n");
|
|
SAY(" -n|--persistent ───────────────────── Don't re-initialize device on timeout. Default: disabled.\n");
|
|
SAY(" -t|--dv-timings ───────────────────── Enable DV-timings querying and events processing");
|
|
SAY(" to automatic resolution change. Default: disabled.\n");
|
|
SAY(" -b|--buffers <N> ──────────────────── The number of buffers to receive data from the device.");
|
|
SAY(" Each buffer may be processed using an independent thread.");
|
|
SAY(" Default: %u (the number of CPU cores (but not more than 4) + 1).\n", cap->n_bufs);
|
|
SAY(" -w|--workers <N> ──────────────────── The number of worker threads but not more than buffers.");
|
|
SAY(" Default: %u (the number of CPU cores (but not more than 4)).\n", enc->n_workers);
|
|
SAY(" -q|--quality <N> ──────────────────── Set quality of JPEG encoding from 1 to 100 (best). Default: %u.", cap->jpeg_quality);
|
|
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 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 MJPEG encoding (default);");
|
|
SAY(" * HW ───────── Use pre-encoded MJPEG frames directly from camera hardware;");
|
|
SAY(" * M2M-VIDEO ── GPU-accelerated MJPEG encoding using V4L2 M2M video interface;");
|
|
SAY(" * M2M-IMAGE ── GPU-accelerated JPEG encoding using V4L2 M2M image interface.\n");
|
|
SAY(" -g|--glitched-resolutions <WxH,...> ─ It doesn't do anything. Still here for compatibility.\n");
|
|
SAY(" -k|--blank <path> ─────────────────── It doesn't do anything. Still here for compatibility.\n");
|
|
SAY(" -K|--last-as-blank <sec> ──────────── It doesn't do anything. Still here for compatibility.\n");
|
|
SAY(" -l|--slowdown ─────────────────────── Slowdown capturing to 1 FPS or less when no stream or sink clients");
|
|
SAY(" are connected. Useful to reduce CPU consumption. Default: disabled.\n");
|
|
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", cap->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: auto select.\n");
|
|
SAY("Image control options:");
|
|
SAY("══════════════════════");
|
|
SAY(" --image-default ────────────────────── Reset all image settings below to default. Default: no change.\n");
|
|
SAY(" --brightness <N|auto|default> ──────── Set brightness. Default: no change.\n");
|
|
SAY(" --contrast <N|default> ─────────────── Set contrast. Default: no change.\n");
|
|
SAY(" --saturation <N|default> ───────────── Set saturation. Default: no change.\n");
|
|
SAY(" --hue <N|auto|default> ─────────────── Set hue. Default: no change.\n");
|
|
SAY(" --gamma <N|default> ─────────────────── Set gamma. Default: no change.\n");
|
|
SAY(" --sharpness <N|default> ────────────── Set sharpness. Default: no change.\n");
|
|
SAY(" --backlight-compensation <N|default> ─ Set backlight compensation. Default: no change.\n");
|
|
SAY(" --white-balance <N|auto|default> ───── Set white balance. Default: no change.\n");
|
|
SAY(" --gain <N|auto|default> ────────────── Set gain. Default: no change.\n");
|
|
SAY(" --color-effect <N|default> ─────────── Set color effect. Default: no change.\n");
|
|
SAY(" --rotate <N|default> ───────────────── Set rotation. Default: no change.\n");
|
|
SAY(" --flip-vertical <1|0|default> ──────── Set vertical flip. Default: no change.\n");
|
|
SAY(" --flip-horizontal <1|0|default> ────── Set horizontal flip. Default: no change.\n");
|
|
SAY(" Hint: use v4l2-ctl --list-ctrls-menus to query available controls of the device.\n");
|
|
SAY("HTTP server options:");
|
|
SAY("════════════════════");
|
|
SAY(" -s|--host <address> ──────── Listen on Hostname or IP. Default: %s.\n", server->host);
|
|
SAY(" -p|--port <N> ────────────── Bind to this TCP port. Default: %u.\n", server->port);
|
|
SAY(" -U|--unix <path> ─────────── Bind to UNIX domain socket. Default: disabled.\n");
|
|
SAY(" -D|--unix-rm ─────────────── Try to remove old UNIX socket file before binding. Default: disabled.\n");
|
|
SAY(" -M|--unix-mode <mode> ────── Set UNIX socket file permissions (like 777). Default: disabled.\n");
|
|
# ifdef WITH_SYSTEMD
|
|
SAY(" -S|--systemd ─────────────── Bind to systemd socket for socket activation.\n");
|
|
# endif
|
|
SAY(" --user <name> ────────────── HTTP basic auth user. Default: disabled.\n");
|
|
SAY(" --passwd <str> ───────────── HTTP basic auth passwd. Default: empty.\n");
|
|
SAY(" --static <path> ───────────── Path to dir with static files instead of embedded root index page.");
|
|
SAY(" Symlinks are not supported for security reasons. Default: disabled.\n");
|
|
SAY(" -e|--drop-same-frames <N> ── Don't send identical frames to clients, but no more than specified number.");
|
|
SAY(" It can significantly reduce the outgoing traffic, but will increase");
|
|
SAY(" the CPU loading. Don't use this option with analog signal sources");
|
|
SAY(" or webcams, it's useless. Default: disabled.\n");
|
|
SAY(" -R|--fake-resolution <WxH> ─ Override image resolution for the /state. Default: disabled.\n");
|
|
SAY(" --tcp-nodelay ────────────── Set TCP_NODELAY flag to the client /stream socket. Only for TCP socket.");
|
|
SAY(" Default: disabled.\n");
|
|
SAY(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
|
|
SAY(" --instance-id <str> ──────── A short string identifier to be displayed in the /state handle.");
|
|
SAY(" It must satisfy regexp ^[a-zA-Z0-9\\./+_-]*$. Default: an empty string.\n");
|
|
SAY(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n", server->timeout);
|
|
# define ADD_SINK(x_name, x_opt) \
|
|
SAY(x_name " sink options:"); \
|
|
SAY("══════════════════"); \
|
|
SAY(" --" x_opt "-sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled."); \
|
|
SAY(" The name should end with a suffix \"." x_opt "\" or \"." x_opt "\"."); \
|
|
SAY(" Default: disabled.\n"); \
|
|
SAY(" --" x_opt "-sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
|
SAY(" --" x_opt "-sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
|
SAY(" --" x_opt "-sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
|
SAY(" --" x_opt "-sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
|
ADD_SINK("JPEG", "jpeg")
|
|
ADD_SINK("RAW", "raw")
|
|
ADD_SINK("H264", "h264")
|
|
# undef ADD_SINK
|
|
SAY(" --h264-bitrate <kbps> ───────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
|
|
SAY(" --h264-gop <N> ──────────────── Interval between keyframes. Default: %u.\n", stream->h264_gop);
|
|
SAY(" --h264-m2m-device </dev/path> ─ Path to V4L2 M2M encoder device. Default: auto select.\n");
|
|
SAY(" --h264-boost ────────────────── Increase encoder performance on PiKVM V4. Default: disabled.\n");
|
|
# ifdef WITH_V4P
|
|
SAY("Passthrough options for PiKVM V4:");
|
|
SAY("═════════════════════════════════");
|
|
SAY(" --v4p ─ Enable HDMI passthrough to OUT2 on the device: https://docs.pikvm.org/pass");
|
|
SAY(" Default: disabled.\n");
|
|
# endif
|
|
# ifdef WITH_GPIO
|
|
SAY("GPIO options:");
|
|
SAY("═════════════");
|
|
SAY(" --gpio-device </dev/path> ───── Path to GPIO character device. Default: %s.\n", us_g_gpio.path);
|
|
SAY(" --gpio-consumer-prefix <str> ── Consumer prefix for GPIO outputs. Default: %s.\n", us_g_gpio.consumer_prefix);
|
|
SAY(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n");
|
|
SAY(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled.\n");
|
|
SAY(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n");
|
|
# endif
|
|
# if (defined(WITH_PDEATHSIG) || defined(WITH_SETPROCTITLE))
|
|
SAY("Process options:");
|
|
SAY("════════════════");
|
|
# endif
|
|
# ifdef WITH_PDEATHSIG
|
|
SAY(" --exit-on-parent-death ─────── Exit the program if the parent process is dead. Default: disabled.\n");
|
|
# endif
|
|
SAY(" --exit-on-no-clients <sec> ──── Exit the program if there have been no stream or sink clients");
|
|
SAY(" or any HTTP requests in the last N seconds. Default: 0 (disabled)\n");
|
|
# ifdef WITH_SETPROCTITLE
|
|
SAY(" --process-name-prefix <str> ── Set process name prefix which will be displayed in the process list");
|
|
SAY(" like '<str>: ustreamer --blah-blah-blah'. Default: disabled.\n");
|
|
SAY(" --notify-parent ────────────── Send SIGUSR2 to the parent process when the stream parameters are changed.");
|
|
SAY(" Checking changes is performed for the online flag and image resolution.\n");
|
|
# endif
|
|
SAY("Logging options:");
|
|
SAY("════════════════");
|
|
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");
|
|
SAY(" Enabling debugging messages can slow down the program.");
|
|
SAY(" Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
|
|
SAY(" Default: %d.\n", us_g_log_level);
|
|
SAY(" --perf ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
|
|
SAY(" --verbose ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
|
|
SAY(" --debug ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
|
|
SAY(" --force-log-colors ─ Force color logging. Default: colored if stderr is a TTY.\n");
|
|
SAY(" --no-log-colors ──── Disable color logging. Default: ditto.\n");
|
|
SAY("Help options:");
|
|
SAY("═════════════");
|
|
SAY(" -h|--help ─────── Print this text and exit.\n");
|
|
SAY(" -v|--version ──── Print version and exit.\n");
|
|
SAY(" --features ────── Print list of supported features.\n");
|
|
# undef SAY
|
|
}
|