mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 04:06:30 +00:00
refactoring
This commit is contained in:
@@ -22,16 +22,34 @@
|
||||
|
||||
#include "m2m.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "../libs/types.h"
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/xioctl.h"
|
||||
|
||||
|
||||
static us_m2m_encoder_s *_m2m_encoder_init(
|
||||
const char *name, const char *path, unsigned output_format,
|
||||
unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma);
|
||||
const char *name, const char *path, uint output_format,
|
||||
uint fps, uint bitrate, uint gop, uint quality, bool allow_dma);
|
||||
|
||||
static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame);
|
||||
static void _m2m_encoder_ensure(us_m2m_encoder_s *enc, const us_frame_s *frame);
|
||||
|
||||
static int _m2m_encoder_init_buffers(
|
||||
us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
|
||||
us_m2m_buffer_s **bufs_ptr, uint *n_bufs_ptr, bool dma);
|
||||
|
||||
static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc);
|
||||
|
||||
@@ -44,10 +62,8 @@ static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *sr
|
||||
#define _E_LOG_VERBOSE(x_msg, ...) US_LOG_VERBOSE("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
#define _E_LOG_DEBUG(x_msg, ...) US_LOG_DEBUG("%s: " x_msg, enc->name, ##__VA_ARGS__)
|
||||
|
||||
#define _RUN(x_next) enc->run->x_next
|
||||
|
||||
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) {
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, uint bitrate, uint gop) {
|
||||
// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
|
||||
// По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480
|
||||
// енкодер через несколько секунд перестает производить корректные фреймы.
|
||||
@@ -55,7 +71,7 @@ us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, u
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_H264, 30, bitrate, gop, 0, true);
|
||||
}
|
||||
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, uint quality) {
|
||||
const double b_min = 25;
|
||||
const double b_max = 20000;
|
||||
const double step = 25;
|
||||
@@ -67,7 +83,7 @@ us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path,
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, 30, bitrate, 0, 0, true);
|
||||
}
|
||||
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) {
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, uint quality) {
|
||||
// FIXME: DMA не работает
|
||||
return _m2m_encoder_init(name, path, V4L2_PIX_FMT_JPEG, 30, 0, 0, quality, false);
|
||||
}
|
||||
@@ -81,22 +97,18 @@ void us_m2m_encoder_destroy(us_m2m_encoder_s *enc) {
|
||||
}
|
||||
|
||||
int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
us_frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
|
||||
|
||||
if (
|
||||
_RUN(width) != src->width
|
||||
|| _RUN(height) != src->height
|
||||
|| _RUN(input_format) != src->format
|
||||
|| _RUN(stride) != src->stride
|
||||
|| _RUN(dma) != (enc->allow_dma && src->dma_fd >= 0)
|
||||
) {
|
||||
_m2m_encoder_prepare(enc, src);
|
||||
}
|
||||
if (!_RUN(ready)) { // Already prepared but failed
|
||||
_m2m_encoder_ensure(enc, src);
|
||||
if (!run->ready) { // Already prepared but failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || _RUN(last_online) != src->online));
|
||||
force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || run->last_online != src->online));
|
||||
|
||||
_E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
|
||||
|
||||
if (_m2m_encoder_compress_raw(enc, src, dest, force_key) < 0) {
|
||||
_m2m_encoder_cleanup(enc);
|
||||
@@ -109,13 +121,13 @@ int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_fra
|
||||
_E_LOG_VERBOSE("Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
dest->used, dest->encode_end_ts - dest->encode_begin_ts, force_key);
|
||||
|
||||
_RUN(last_online) = src->online;
|
||||
run->last_online = src->online;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static us_m2m_encoder_s *_m2m_encoder_init(
|
||||
const char *name, const char *path, unsigned output_format,
|
||||
unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma) {
|
||||
const char *name, const char *path, uint output_format,
|
||||
uint fps, uint bitrate, uint gop, uint quality, bool allow_dma) {
|
||||
|
||||
US_LOG_INFO("%s: Initializing encoder ...", name);
|
||||
|
||||
@@ -143,30 +155,48 @@ static us_m2m_encoder_s *_m2m_encoder_init(
|
||||
}
|
||||
|
||||
#define _E_XIOCTL(x_request, x_value, x_msg, ...) { \
|
||||
if (us_xioctl(_RUN(fd), x_request, x_value) < 0) { \
|
||||
if (us_xioctl(run->fd, x_request, x_value) < 0) { \
|
||||
_E_LOG_PERROR(x_msg, ##__VA_ARGS__); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame) {
|
||||
static void _m2m_encoder_ensure(us_m2m_encoder_s *enc, const us_frame_s *frame) {
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
const bool dma = (enc->allow_dma && frame->dma_fd >= 0);
|
||||
if (
|
||||
run->p_width == frame->width
|
||||
&& run->p_height == frame->height
|
||||
&& run->p_input_format == frame->format
|
||||
&& run->p_stride == frame->stride
|
||||
&& run->p_dma == dma
|
||||
) {
|
||||
return; // Configured already
|
||||
}
|
||||
|
||||
_E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
|
||||
|
||||
_E_LOG_DEBUG("Encoder changes: width=%u->%u, height=%u->%u, input_format=%u->%u, stride=%u->%u, dma=%u->%u",
|
||||
run->p_width, frame->width,
|
||||
run->p_height, frame->height,
|
||||
run->p_input_format, frame->format,
|
||||
run->p_stride, frame->stride,
|
||||
run->p_dma, dma);
|
||||
|
||||
_m2m_encoder_cleanup(enc);
|
||||
|
||||
_RUN(width) = frame->width;
|
||||
_RUN(height) = frame->height;
|
||||
_RUN(input_format) = frame->format;
|
||||
_RUN(stride) = frame->stride;
|
||||
_RUN(dma) = dma;
|
||||
run->p_width = frame->width;
|
||||
run->p_height = frame->height;
|
||||
run->p_input_format = frame->format;
|
||||
run->p_stride = frame->stride;
|
||||
run->p_dma = dma;
|
||||
|
||||
if ((_RUN(fd) = open(enc->path, O_RDWR)) < 0) {
|
||||
if ((run->fd = open(enc->path, O_RDWR)) < 0) {
|
||||
_E_LOG_PERROR("Can't open encoder device");
|
||||
goto error;
|
||||
}
|
||||
_E_LOG_DEBUG("Encoder device fd=%d opened", _RUN(fd));
|
||||
_E_LOG_DEBUG("Encoder device fd=%d opened", run->fd);
|
||||
|
||||
# define SET_OPTION(x_cid, x_value) { \
|
||||
struct v4l2_control m_ctl = {0}; \
|
||||
@@ -175,12 +205,11 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
_E_LOG_DEBUG("Configuring option " #x_cid " ..."); \
|
||||
_E_XIOCTL(VIDIOC_S_CTRL, &m_ctl, "Can't set option " #x_cid); \
|
||||
}
|
||||
|
||||
if (enc->output_format == V4L2_PIX_FMT_H264) {
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE, enc->bitrate);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, enc->gop);
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE);
|
||||
if (_RUN(width) * _RUN(height) <= 1920 * 1080) { // https://forums.raspberrypi.com/viewtopic.php?t=291447#p1762296
|
||||
if (run->p_width * run->p_height <= 1920 * 1080) { // https://forums.raspberrypi.com/viewtopic.php?t=291447#p1762296
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
|
||||
} else {
|
||||
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
|
||||
@@ -193,19 +222,18 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
} else if (enc->output_format == V4L2_PIX_FMT_JPEG) {
|
||||
SET_OPTION(V4L2_CID_JPEG_COMPRESSION_QUALITY, enc->quality);
|
||||
}
|
||||
|
||||
# undef SET_OPTION
|
||||
|
||||
{
|
||||
struct v4l2_format fmt = {0};
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
fmt.fmt.pix_mp.width = _RUN(width);
|
||||
fmt.fmt.pix_mp.height = _RUN(height);
|
||||
fmt.fmt.pix_mp.pixelformat = _RUN(input_format);
|
||||
fmt.fmt.pix_mp.width = run->p_width;
|
||||
fmt.fmt.pix_mp.height = run->p_height;
|
||||
fmt.fmt.pix_mp.pixelformat = run->p_input_format;
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG; // libcamera currently has no means to request the right colour space
|
||||
fmt.fmt.pix_mp.num_planes = 1;
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = _RUN(stride);
|
||||
// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = run->p_stride;
|
||||
_E_LOG_DEBUG("Configuring INPUT format ...");
|
||||
_E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
|
||||
}
|
||||
@@ -213,8 +241,8 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
{
|
||||
struct v4l2_format fmt = {0};
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
fmt.fmt.pix_mp.width = _RUN(width);
|
||||
fmt.fmt.pix_mp.height = _RUN(height);
|
||||
fmt.fmt.pix_mp.width = run->p_width;
|
||||
fmt.fmt.pix_mp.height = run->p_height;
|
||||
fmt.fmt.pix_mp.pixelformat = enc->output_format;
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
|
||||
@@ -245,12 +273,16 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
_E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
|
||||
}
|
||||
|
||||
if (_m2m_encoder_init_buffers(enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
&_RUN(input_bufs), &_RUN(n_input_bufs), dma) < 0) {
|
||||
if (_m2m_encoder_init_buffers(
|
||||
enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
&run->input_bufs, &run->n_input_bufs, dma
|
||||
) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (_m2m_encoder_init_buffers(enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
&_RUN(output_bufs), &_RUN(n_output_bufs), false) < 0) {
|
||||
if (_m2m_encoder_init_buffers(
|
||||
enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
&run->output_bufs, &run->n_output_bufs, false
|
||||
) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -264,18 +296,20 @@ static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame)
|
||||
_E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
|
||||
}
|
||||
|
||||
_RUN(ready) = true;
|
||||
_E_LOG_DEBUG("Encoder state: *** READY ***");
|
||||
run->ready = true;
|
||||
_E_LOG_INFO("Encoder is ready");
|
||||
return;
|
||||
|
||||
error:
|
||||
_m2m_encoder_cleanup(enc);
|
||||
_E_LOG_ERROR("Encoder destroyed due an error (prepare)");
|
||||
error:
|
||||
_m2m_encoder_cleanup(enc);
|
||||
_E_LOG_ERROR("Encoder destroyed due an error (prepare)");
|
||||
}
|
||||
|
||||
static int _m2m_encoder_init_buffers(
|
||||
us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
|
||||
us_m2m_buffer_s **bufs_ptr, uint *n_bufs_ptr, bool dma) {
|
||||
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
_E_LOG_DEBUG("Initializing %s buffers ...", name);
|
||||
|
||||
@@ -294,98 +328,102 @@ static int _m2m_encoder_init_buffers(
|
||||
|
||||
if (dma) {
|
||||
*n_bufs_ptr = req.count;
|
||||
} else {
|
||||
US_CALLOC(*bufs_ptr, req.count);
|
||||
for (*n_bufs_ptr = 0; *n_bufs_ptr < req.count; ++(*n_bufs_ptr)) {
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane plane = {0};
|
||||
buf.type = type;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = *n_bufs_ptr;
|
||||
buf.length = 1;
|
||||
buf.m.planes = &plane;
|
||||
|
||||
_E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
|
||||
|
||||
_E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
if (((*bufs_ptr)[*n_bufs_ptr].data = mmap(
|
||||
NULL,
|
||||
plane.length,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
_RUN(fd),
|
||||
plane.m.mem_offset
|
||||
)) == MAP_FAILED) {
|
||||
_E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
|
||||
goto error;
|
||||
}
|
||||
assert((*bufs_ptr)[*n_bufs_ptr].data != NULL);
|
||||
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
|
||||
|
||||
_E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
US_CALLOC(*bufs_ptr, req.count);
|
||||
for (*n_bufs_ptr = 0; *n_bufs_ptr < req.count; ++(*n_bufs_ptr)) {
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane plane = {0};
|
||||
buf.type = type;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = *n_bufs_ptr;
|
||||
buf.length = 1;
|
||||
buf.m.planes = &plane;
|
||||
|
||||
_E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
|
||||
|
||||
_E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
if (((*bufs_ptr)[*n_bufs_ptr].data = mmap(
|
||||
NULL, plane.length,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
run->fd, plane.m.mem_offset
|
||||
)) == MAP_FAILED) {
|
||||
_E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
|
||||
goto error;
|
||||
}
|
||||
assert((*bufs_ptr)[*n_bufs_ptr].data != NULL);
|
||||
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
|
||||
|
||||
_E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
|
||||
}
|
||||
_E_LOG_DEBUG("All %s buffers are ready", name);
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
|
||||
error: // Mostly for _E_XIOCTL
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc) {
|
||||
if (_RUN(ready)) {
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
bool say = false;
|
||||
|
||||
if (run->ready) {
|
||||
say = true;
|
||||
# define STOP_STREAM(x_name, x_type) { \
|
||||
enum v4l2_buf_type m_type_var = x_type; \
|
||||
_E_LOG_DEBUG("Stopping %s ...", x_name); \
|
||||
if (us_xioctl(_RUN(fd), VIDIOC_STREAMOFF, &m_type_var) < 0) { \
|
||||
if (us_xioctl(run->fd, VIDIOC_STREAMOFF, &m_type_var) < 0) { \
|
||||
_E_LOG_PERROR("Can't stop %s", x_name); \
|
||||
} \
|
||||
}
|
||||
|
||||
STOP_STREAM("OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
||||
STOP_STREAM("INPUT", V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
||||
|
||||
# undef STOP_STREAM
|
||||
}
|
||||
|
||||
# define DESTROY_BUFFERS(x_name, x_target) { \
|
||||
if (_RUN(x_target##_bufs) != NULL) { \
|
||||
for (unsigned m_index = 0; m_index < _RUN(n_##x_target##_bufs); ++m_index) { \
|
||||
if (_RUN(x_target##_bufs[m_index].allocated) > 0 && _RUN(x_target##_bufs[m_index].data) != NULL) { \
|
||||
if (munmap(_RUN(x_target##_bufs[m_index].data), _RUN(x_target##_bufs[m_index].allocated)) < 0) { \
|
||||
# define DELETE_BUFFERS(x_name, x_target) { \
|
||||
if (run->x_target##_bufs != NULL) { \
|
||||
say = true; \
|
||||
for (uint m_index = 0; m_index < run->n_##x_target##_bufs; ++m_index) { \
|
||||
us_m2m_buffer_s *m_buf = &run->x_target##_bufs[m_index]; \
|
||||
if (m_buf->allocated > 0 && m_buf->data != NULL) { \
|
||||
if (munmap(m_buf->data, m_buf->allocated) < 0) { \
|
||||
_E_LOG_PERROR("Can't unmap %s buffer=%u", #x_name, m_index); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
free(_RUN(x_target##_bufs)); \
|
||||
_RUN(x_target##_bufs) = NULL; \
|
||||
US_DELETE(run->x_target##_bufs, free); \
|
||||
} \
|
||||
_RUN(n_##x_target##_bufs) = 0; \
|
||||
run->n_##x_target##_bufs = 0; \
|
||||
}
|
||||
DELETE_BUFFERS("OUTPUT", output);
|
||||
DELETE_BUFFERS("INPUT", input);
|
||||
# undef DELETE_BUFFERS
|
||||
|
||||
DESTROY_BUFFERS("OUTPUT", output);
|
||||
DESTROY_BUFFERS("INPUT", input);
|
||||
|
||||
# undef DESTROY_BUFFERS
|
||||
|
||||
if (_RUN(fd) >= 0) {
|
||||
if (close(_RUN(fd)) < 0) {
|
||||
if (run->fd >= 0) {
|
||||
say = true;
|
||||
if (close(run->fd) < 0) {
|
||||
_E_LOG_PERROR("Can't close encoder device");
|
||||
}
|
||||
_RUN(fd) = -1;
|
||||
run->fd = -1;
|
||||
}
|
||||
|
||||
_RUN(last_online) = -1;
|
||||
_RUN(ready) = false;
|
||||
run->last_online = -1;
|
||||
run->ready = false;
|
||||
|
||||
_E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
|
||||
if (say) {
|
||||
_E_LOG_INFO("Encoder closed");
|
||||
}
|
||||
}
|
||||
|
||||
static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
|
||||
assert(_RUN(ready));
|
||||
us_m2m_encoder_runtime_s *const run = enc->run;
|
||||
|
||||
_E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
|
||||
assert(run->ready);
|
||||
|
||||
if (force_key) {
|
||||
struct v4l2_control ctl = {0};
|
||||
@@ -401,7 +439,7 @@ static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *sr
|
||||
input_buf.length = 1;
|
||||
input_buf.m.planes = &input_plane;
|
||||
|
||||
if (_RUN(dma)) {
|
||||
if (run->p_dma) {
|
||||
input_buf.index = 0;
|
||||
input_buf.memory = V4L2_MEMORY_DMABUF;
|
||||
input_buf.field = V4L2_FIELD_NONE;
|
||||
@@ -411,38 +449,38 @@ static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *sr
|
||||
input_buf.memory = V4L2_MEMORY_MMAP;
|
||||
_E_LOG_DEBUG("Grabbing INPUT buffer ...");
|
||||
_E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
|
||||
if (input_buf.index >= _RUN(n_input_bufs)) {
|
||||
if (input_buf.index >= run->n_input_bufs) {
|
||||
_E_LOG_ERROR("V4L2 error: grabbed invalid INPUT: buffer=%u, n_bufs=%u",
|
||||
input_buf.index, _RUN(n_input_bufs));
|
||||
input_buf.index, run->n_input_bufs);
|
||||
goto error;
|
||||
}
|
||||
_E_LOG_DEBUG("Grabbed INPUT buffer=%u", input_buf.index);
|
||||
}
|
||||
|
||||
const uint64_t now = us_get_now_monotonic_u64();
|
||||
const u64 now_ts = us_get_now_monotonic_u64();
|
||||
struct timeval ts = {
|
||||
.tv_sec = now / 1000000,
|
||||
.tv_usec = now % 1000000,
|
||||
.tv_sec = now_ts / 1000000,
|
||||
.tv_usec = now_ts % 1000000,
|
||||
};
|
||||
|
||||
input_buf.timestamp.tv_sec = ts.tv_sec;
|
||||
input_buf.timestamp.tv_usec = ts.tv_usec;
|
||||
input_plane.bytesused = src->used;
|
||||
input_plane.length = src->used;
|
||||
if (!_RUN(dma)) {
|
||||
memcpy(_RUN(input_bufs[input_buf.index].data), src->data, src->used);
|
||||
if (!run->p_dma) {
|
||||
memcpy(run->input_bufs[input_buf.index].data, src->data, src->used);
|
||||
}
|
||||
|
||||
const char *input_name = (_RUN(dma) ? "INPUT-DMA" : "INPUT");
|
||||
const char *input_name = (run->p_dma ? "INPUT-DMA" : "INPUT");
|
||||
|
||||
_E_LOG_DEBUG("Sending%s %s buffer ...", (!_RUN(dma) ? " (releasing)" : ""), input_name);
|
||||
_E_LOG_DEBUG("Sending%s %s buffer ...", (!run->p_dma ? " (releasing)" : ""), input_name);
|
||||
_E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
|
||||
|
||||
// Для не-DMA отправка буфера по факту являтся освобождением этого буфера
|
||||
bool input_released = !_RUN(dma);
|
||||
bool input_released = !run->p_dma;
|
||||
|
||||
while (true) {
|
||||
struct pollfd enc_poll = {_RUN(fd), POLLIN, 0};
|
||||
struct pollfd enc_poll = {run->fd, POLLIN, 0};
|
||||
|
||||
_E_LOG_DEBUG("Polling encoder ...");
|
||||
if (poll(&enc_poll, 1, 1000) < 0 && errno != EINTR) {
|
||||
@@ -474,7 +512,7 @@ static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *sr
|
||||
// входному (с тем же таймстампом).
|
||||
_E_LOG_DEBUG("Need to retry OUTPUT buffer due timestamp mismatch");
|
||||
} else {
|
||||
us_frame_set_data(dest, _RUN(output_bufs[output_buf.index].data), output_plane.bytesused);
|
||||
us_frame_set_data(dest, run->output_bufs[output_buf.index].data, output_plane.bytesused);
|
||||
dest->key = output_buf.flags & V4L2_BUF_FLAG_KEYFRAME;
|
||||
dest->gop = enc->gop;
|
||||
done = true;
|
||||
@@ -488,10 +526,10 @@ static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *sr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
|
||||
error: // Mostly for _E_XIOCTL
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef _E_XIOCTL
|
||||
|
||||
@@ -22,65 +22,49 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/types.h"
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/xioctl.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t allocated;
|
||||
u8 *data;
|
||||
uz allocated;
|
||||
} us_m2m_buffer_s;
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
us_m2m_buffer_s *input_bufs;
|
||||
unsigned n_input_bufs;
|
||||
uint n_input_bufs;
|
||||
us_m2m_buffer_s *output_bufs;
|
||||
unsigned n_output_bufs;
|
||||
uint n_output_bufs;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned input_format;
|
||||
unsigned stride;
|
||||
bool dma;
|
||||
bool ready;
|
||||
uint p_width;
|
||||
uint p_height;
|
||||
uint p_input_format;
|
||||
uint p_stride;
|
||||
bool p_dma;
|
||||
|
||||
int last_online;
|
||||
bool ready;
|
||||
int last_online;
|
||||
} us_m2m_encoder_runtime_s;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *path;
|
||||
unsigned output_format;
|
||||
unsigned fps;
|
||||
unsigned bitrate;
|
||||
unsigned gop;
|
||||
unsigned quality;
|
||||
bool allow_dma;
|
||||
char *name;
|
||||
char *path;
|
||||
uint output_format;
|
||||
uint fps;
|
||||
uint bitrate;
|
||||
uint gop;
|
||||
uint quality;
|
||||
bool allow_dma;
|
||||
|
||||
us_m2m_encoder_runtime_s *run;
|
||||
} us_m2m_encoder_s;
|
||||
|
||||
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop);
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality);
|
||||
us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, uint bitrate, uint gop);
|
||||
us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, uint quality);
|
||||
us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, uint quality);
|
||||
void us_m2m_encoder_destroy(us_m2m_encoder_s *enc);
|
||||
|
||||
int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key);
|
||||
|
||||
Reference in New Issue
Block a user