From e14644572bfb3a0a991701f4cf332ff0291527ce Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Sat, 11 Dec 2021 01:17:34 +0300 Subject: [PATCH] refactoring --- src/ustreamer/encoder.c | 39 +++++-------------------- src/ustreamer/encoder.h | 17 ----------- src/ustreamer/h264/stream.c | 24 +-------------- src/ustreamer/m2m.c | 58 ++++++++++++++++++++++++++++++++++--- src/ustreamer/m2m.h | 5 +++- 5 files changed, 67 insertions(+), 76 deletions(-) diff --git a/src/ustreamer/encoder.c b/src/ustreamer/encoder.c index f838638..a5f0396 100644 --- a/src/ustreamer/encoder.c +++ b/src/ustreamer/encoder.c @@ -115,41 +115,18 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) { n_workers = 1; } else if (type == ENCODER_TYPE_M2M_VIDEO || type == ENCODER_TYPE_M2M_IMAGE) { - LOG_DEBUG("Preparing M2M encoder ..."); + LOG_DEBUG("Preparing M2M-%s encoder ...", (type == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE")); if (ER(m2ms) == NULL) { A_CALLOC(ER(m2ms), n_workers); } - - // Начинаем с нуля и доинициализируем на следующих заходах при необходимости - if (ER(n_m2ms) < n_workers) { + for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) { + // Начинаем с нуля и доинициализируем на следующих заходах при необходимости + char name[32]; + snprintf(name, 32, "JPEG-%u", ER(n_m2ms)); if (type == ENCODER_TYPE_M2M_VIDEO) { - 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); - } - + ER(m2ms[ER(n_m2ms)]) = m2m_mjpeg_encoder_init(name, enc->m2m_path, quality); } 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); - } + ER(m2ms[ER(n_m2ms)]) = m2m_jpeg_encoder_init(name, enc->m2m_path, quality); } } @@ -238,7 +215,7 @@ static bool _worker_run_job(worker_s *wr) { hw_encoder_compress(src, dest); } else if (ER(type) == ENCODER_TYPE_M2M_VIDEO || ER(type) == ENCODER_TYPE_M2M_IMAGE) { - LOG_VERBOSE("Compressing buffer using M2M"); + LOG_VERBOSE("Compressing buffer using M2M-%s", (ER(type) == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE")); if (m2m_encoder_ensure_ready(ER(m2ms[wr->number]), src) < 0) { goto error; } diff --git a/src/ustreamer/encoder.h b/src/ustreamer/encoder.h index 38e6bce..528c911 100644 --- a/src/ustreamer/encoder.h +++ b/src/ustreamer/encoder.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -44,22 +43,6 @@ #include "encoders/hw/encoder.h" -#ifndef CFG_ENCODER_M2M_BITRATE_MIN -# define CFG_ENCODER_M2M_BITRATE_MIN 25 -#endif -#define ENCODER_M2M_BITRATE_MIN ((unsigned)CFG_ENCODER_M2M_BITRATE_MIN) - -#ifndef CFG_ENCODER_M2M_BITRATE_MAX -# define CFG_ENCODER_M2M_BITRATE_MAX 25000 -#endif -#define ENCODER_M2M_BITRATE_MAX ((unsigned)CFG_ENCODER_M2M_BITRATE_MAX) - -#ifndef CFG_ENCODER_M2M_BITRATE_STEP -# define CFG_ENCODER_M2M_BITRATE_STEP 25 -#endif -#define ENCODER_M2M_BITRATE_STEP ((unsigned)CFG_ENCODER_M2M_BITRATE_STEP) - - #define ENCODER_TYPES_STR "CPU, HW, M2M-VIDEO, M2M-IMAGE, NOOP" typedef enum { diff --git a/src/ustreamer/h264/stream.c b/src/ustreamer/h264/stream.c index b525ed3..41e69b3 100644 --- a/src/ustreamer/h264/stream.c +++ b/src/ustreamer/h264/stream.c @@ -30,29 +30,7 @@ h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitr h264->tmp_src = frame_init(); h264->dest = frame_init(); atomic_init(&h264->online, false); - -# define OPTION(_required, _key, _value) {#_key, _required, V4L2_CID_MPEG_VIDEO_##_key, _value} - - m2m_option_s options[] = { - OPTION(true, BITRATE, bitrate * 1000), - OPTION(false, BITRATE_PEAK, bitrate * 1000), - OPTION(true, H264_I_PERIOD, gop), - OPTION(true, H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE), - OPTION(true, H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0), - OPTION(true, REPEAT_SEQ_HEADER, 1), - OPTION(false, H264_MIN_QP, 16), - OPTION(false, H264_MAX_QP, 32), - {NULL, false, 0, 0}, - }; - -# undef OPTION - - // FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100 - // По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480 - // енкодер через несколько секунд перестает производить корректные фреймы. - // TODO: Это было актуально для MMAL, надо проверить для V4L2. - - h264->enc = m2m_encoder_init("H264", path, V4L2_PIX_FMT_H264, 30, options); + h264->enc = m2m_h264_encoder_init("H264", path, bitrate, gop); return h264; } diff --git a/src/ustreamer/m2m.c b/src/ustreamer/m2m.c index 14148bf..cf2cfc3 100644 --- a/src/ustreamer/m2m.c +++ b/src/ustreamer/m2m.c @@ -23,6 +23,8 @@ #include "m2m.h" +static m2m_encoder_s *_m2m_encoder_init(const char *name, const char *path, unsigned format, unsigned fps, m2m_option_s *options); + static bool _m2m_encoder_is_prepared_for(m2m_encoder_s *enc, const frame_s *frame); static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame); @@ -42,9 +44,57 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra #define E_LOG_DEBUG(_msg, ...) LOG_DEBUG("%s: " _msg, enc->name, ##__VA_ARGS__) -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); +m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) { +# define OPTION(_required, _key, _value) {#_key, _required, V4L2_CID_MPEG_VIDEO_##_key, _value} + m2m_option_s options[] = { + OPTION(true, BITRATE, bitrate * 1000), + // OPTION(false, BITRATE_PEAK, bitrate * 1000), + OPTION(true, H264_I_PERIOD, gop), + OPTION(true, H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE), + OPTION(true, H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0), + OPTION(true, REPEAT_SEQ_HEADER, 1), + OPTION(false, H264_MIN_QP, 16), + OPTION(false, H264_MAX_QP, 32), + {NULL, false, 0, 0}, + }; + +# undef OPTION + + // FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100 + // По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480 + // енкодер через несколько секунд перестает производить корректные фреймы. + return _m2m_encoder_init(name, path, V4L2_PIX_FMT_H264, 30, options); +} + +m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) { + const double b_min = 25; + const double b_max = 25000; + const double step = 25; + double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min; + bitrate = step * round(bitrate / step); + bitrate *= 1000; // From Kbps + assert(bitrate > 0); + + m2m_option_s options[] = { + {"BITRATE", true, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate}, + {NULL, false, 0, 0}, + }; + + // FIXME: То же самое про 30 or 0, но еще даже не проверено на низких разрешениях + return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, 30, options); +} + +m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) { + m2m_option_s options[] = { + {"QUALITY", true, V4L2_CID_JPEG_COMPRESSION_QUALITY, quality}, + {NULL, false, 0, 0}, + }; + + return _m2m_encoder_init(name, path, V4L2_PIX_FMT_JPEG, 30, options); +} + +static m2m_encoder_s *_m2m_encoder_init(const char *name, const char *path, unsigned format, unsigned fps, m2m_option_s *options) { LOG_INFO("%s: Initializing encoder ...", name); m2m_encoder_s *enc; @@ -159,8 +209,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) { diff --git a/src/ustreamer/m2m.h b/src/ustreamer/m2m.h index cacc3cf..9efc5e4 100644 --- a/src/ustreamer/m2m.h +++ b/src/ustreamer/m2m.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -78,7 +79,9 @@ typedef struct { } m2m_encoder_s; -m2m_encoder_s *m2m_encoder_init(const char *name, const char *path, unsigned format, unsigned fps, m2m_option_s *options); +m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop); +m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality); +m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality); void m2m_encoder_destroy(m2m_encoder_s *enc); int m2m_encoder_ensure_ready(m2m_encoder_s *enc, const frame_s *frame);