mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 04:06:30 +00:00
common m2m code, fixed non-dma dq
This commit is contained in:
@@ -35,14 +35,30 @@ h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitr
|
||||
// По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480
|
||||
// енкодер через несколько секунд перестает производить корректные фреймы.
|
||||
// TODO: Это было актуально для MMAL, надо проверить для V4L2.
|
||||
h264->enc = h264_encoder_init(path, bitrate, gop, 30);
|
||||
|
||||
# define ADD_OPTION(_index, _required, _key, _value) { \
|
||||
h264->options[_index] = (m2m_option_s){#_key, _required, V4L2_CID_MPEG_VIDEO_##_key, _value}; \
|
||||
}
|
||||
|
||||
A_CALLOC(h264->options, 8);
|
||||
ADD_OPTION(0, true, BITRATE, bitrate * 1000);
|
||||
ADD_OPTION(1, true, H264_I_PERIOD, gop);
|
||||
ADD_OPTION(2, true, H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE);
|
||||
ADD_OPTION(3, true, H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
|
||||
ADD_OPTION(4, true, REPEAT_SEQ_HEADER, 1);
|
||||
ADD_OPTION(5, false, H264_MIN_QP, 16);
|
||||
ADD_OPTION(6, false, H264_MAX_QP, 32);
|
||||
h264->options[7] = (m2m_option_s){NULL, false, 0, 0};
|
||||
|
||||
# undef ADD_OPTION
|
||||
|
||||
h264->enc = m2m_encoder_init("H264", path, V4L2_PIX_FMT_H264, 30, h264->options);
|
||||
return h264;
|
||||
}
|
||||
|
||||
void h264_stream_destroy(h264_stream_s *h264) {
|
||||
if (h264->enc) {
|
||||
h264_encoder_destroy(h264->enc);
|
||||
}
|
||||
m2m_encoder_destroy(h264->enc);
|
||||
free(h264->options);
|
||||
frame_destroy(h264->dest);
|
||||
frame_destroy(h264->tmp_src);
|
||||
free(h264);
|
||||
@@ -76,12 +92,12 @@ void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int dma_fd,
|
||||
|
||||
bool online = false;
|
||||
|
||||
if (!h264_encoder_is_prepared_for(h264->enc, frame, dma)) {
|
||||
h264_encoder_prepare(h264->enc, frame, dma);
|
||||
if (!m2m_encoder_is_prepared_for(h264->enc, frame, dma)) {
|
||||
m2m_encoder_prepare(h264->enc, frame, dma);
|
||||
}
|
||||
|
||||
if (h264->enc->ready) {
|
||||
if (h264_encoder_compress(h264->enc, frame, dma_fd, h264->dest, force_key) == 0) {
|
||||
if (m2m_encoder_compress(h264->enc, frame, dma_fd, h264->dest, force_key) == 0) {
|
||||
online = !memsink_server_put(h264->sink, h264->dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,15 @@
|
||||
#include "../../libs/frame.h"
|
||||
#include "../../libs/memsink.h"
|
||||
#include "../../libs/unjpeg.h"
|
||||
|
||||
#include "encoder.h"
|
||||
#include "../m2m.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
memsink_s *sink;
|
||||
frame_s *tmp_src;
|
||||
frame_s *dest;
|
||||
h264_encoder_s *enc;
|
||||
m2m_option_s *options;
|
||||
m2m_encoder_s *enc;
|
||||
atomic_bool online;
|
||||
} h264_stream_s;
|
||||
|
||||
|
||||
@@ -20,59 +20,65 @@
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "encoder.h"
|
||||
#include "m2m.h"
|
||||
|
||||
|
||||
static int _h264_encoder_init_buffers(
|
||||
h264_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
h264_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
|
||||
static int _m2m_encoder_init_buffers(
|
||||
m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
|
||||
|
||||
static void _h264_encoder_cleanup(h264_encoder_s *enc);
|
||||
static void _m2m_encoder_cleanup(m2m_encoder_s *enc);
|
||||
|
||||
static int _h264_encoder_compress_raw(
|
||||
h264_encoder_s *enc, const frame_s *src, int src_dma_fd,
|
||||
static int _m2m_encoder_compress_raw(
|
||||
m2m_encoder_s *enc, const frame_s *src, int src_dma_fd,
|
||||
frame_s *dest, bool force_key);
|
||||
|
||||
|
||||
h264_encoder_s *h264_encoder_init(const char *path, unsigned bitrate, unsigned gop, unsigned fps) {
|
||||
LOG_INFO("H264: Initializing V4L2 encoder ...");
|
||||
LOG_INFO("H264: Using bitrate: %u Kbps", bitrate);
|
||||
LOG_INFO("H264: Using GOP: %u", gop);
|
||||
#define E_LOG_ERROR(_msg, ...) LOG_ERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_PERROR(_msg, ...) LOG_PERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_INFO(_msg, ...) LOG_INFO("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_VERBOSE(_msg, ...) LOG_VERBOSE("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
#define E_LOG_DEBUG(_msg, ...) LOG_DEBUG("%s: " _msg, enc->name, ##__VA_ARGS__)
|
||||
|
||||
h264_encoder_s *enc;
|
||||
|
||||
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;
|
||||
A_CALLOC(enc, 1);
|
||||
assert(enc->name = strdup(name));
|
||||
assert(enc->path = strdup(path));
|
||||
enc->bitrate = bitrate; // Kbps
|
||||
enc->gop = gop; // Interval between keyframes
|
||||
enc->output_format = format;
|
||||
enc->fps = fps;
|
||||
enc->options = options;
|
||||
enc->last_online = -1;
|
||||
return enc;
|
||||
}
|
||||
|
||||
void h264_encoder_destroy(h264_encoder_s *enc) {
|
||||
LOG_INFO("H264: Destroying encoder ...");
|
||||
_h264_encoder_cleanup(enc);
|
||||
void m2m_encoder_destroy(m2m_encoder_s *enc) {
|
||||
E_LOG_INFO("Destroying encoder ...");
|
||||
_m2m_encoder_cleanup(enc);
|
||||
free(enc->path);
|
||||
free(enc->name);
|
||||
free(enc);
|
||||
}
|
||||
|
||||
bool h264_encoder_is_prepared_for(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
bool m2m_encoder_is_prepared_for(m2m_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
# define EQ(_field) (enc->_field == frame->_field)
|
||||
return (EQ(width) && EQ(height) && EQ(format) && EQ(stride) && (enc->dma == dma));
|
||||
# undef EQ
|
||||
}
|
||||
|
||||
#define ENCODER_XIOCTL(_request, _value, _msg, ...) { \
|
||||
#define E_XIOCTL(_request, _value, _msg, ...) { \
|
||||
if (xioctl(enc->fd, _request, _value) < 0) { \
|
||||
LOG_PERROR(_msg, ##__VA_ARGS__); \
|
||||
E_LOG_PERROR(_msg, ##__VA_ARGS__); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
LOG_INFO("H264: Configuring encoder: DMA=%d ...", dma);
|
||||
int m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
|
||||
|
||||
_h264_encoder_cleanup(enc);
|
||||
_m2m_encoder_cleanup(enc);
|
||||
|
||||
enc->width = frame->width;
|
||||
enc->height = frame->height;
|
||||
@@ -81,38 +87,27 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
enc->dma = dma;
|
||||
|
||||
if ((enc->fd = open(enc->path, O_RDWR)) < 0) {
|
||||
LOG_PERROR("H264: Can't open encoder device");
|
||||
E_LOG_PERROR("Can't open encoder device");
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
# define SET_OPTION(_required, _key, _value) { \
|
||||
struct v4l2_control _ctl = {0}; \
|
||||
_ctl.id = V4L2_CID_MPEG_VIDEO_##_key; \
|
||||
_ctl.value = _value; \
|
||||
LOG_DEBUG("H264: Configuring option %s ...", #_key); \
|
||||
if (_required) { \
|
||||
ENCODER_XIOCTL(VIDIOC_S_CTRL, &_ctl, "H264: Can't set option " #_key); \
|
||||
} else { \
|
||||
if (xioctl(enc->fd, VIDIOC_S_CTRL, &_ctl) < 0) { \
|
||||
if (errno == EINVAL) { \
|
||||
LOG_ERROR("H264: Can't set option " #_key ": Unsupported by encoder"); \
|
||||
} else { \
|
||||
LOG_PERROR("H264: Can't set option " #_key); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
for (m2m_option_s *option = enc->options; option->name != NULL; ++option) {
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = option->id;
|
||||
ctl.value = option->value;
|
||||
|
||||
E_LOG_DEBUG("Configuring option %s ...", option->name);
|
||||
if (option->required) {
|
||||
E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't set option %s", option->name);
|
||||
} else {
|
||||
if (xioctl(enc->fd, VIDIOC_S_CTRL, &ctl) < 0) {
|
||||
if (errno == EINVAL) {
|
||||
E_LOG_ERROR("Can't set option %s: Unsupported by encoder", option->name);
|
||||
} else {
|
||||
E_LOG_PERROR("Can't set option %s", option->name);
|
||||
}
|
||||
}
|
||||
|
||||
SET_OPTION(true, BITRATE, enc->bitrate * 1000);
|
||||
SET_OPTION(true, H264_I_PERIOD, enc->gop);
|
||||
SET_OPTION(true, H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE);
|
||||
SET_OPTION(true, H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
|
||||
SET_OPTION(true, REPEAT_SEQ_HEADER, 1);
|
||||
SET_OPTION(false, H264_MIN_QP, 16);
|
||||
SET_OPTION(false, H264_MAX_QP, 32);
|
||||
|
||||
# undef SET_OPTION
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -124,8 +119,8 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
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;
|
||||
LOG_DEBUG("H264: Configuring INPUT format ...");
|
||||
ENCODER_XIOCTL(VIDIOC_S_FMT, &fmt, "H264: Can't set INPUT format");
|
||||
E_LOG_DEBUG("Configuring INPUT format ...");
|
||||
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -133,14 +128,14 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
fmt.fmt.pix_mp.width = frame->width;
|
||||
fmt.fmt.pix_mp.height = frame->height;
|
||||
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
|
||||
fmt.fmt.pix_mp.pixelformat = enc->output_format;
|
||||
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;
|
||||
LOG_DEBUG("H264: Configuring OUTPUT format ...");
|
||||
ENCODER_XIOCTL(VIDIOC_S_FMT, &fmt, "H264: Can't set OUTPUT format");
|
||||
E_LOG_DEBUG("Configuring OUTPUT format ...");
|
||||
E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -148,57 +143,57 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma) {
|
||||
setfps.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
setfps.parm.output.timeperframe.numerator = 1;
|
||||
setfps.parm.output.timeperframe.denominator = enc->fps;
|
||||
LOG_DEBUG("H264: Configuring INPUT FPS ...");
|
||||
ENCODER_XIOCTL(VIDIOC_S_PARM, &setfps, "H264: Can't set INPUT FPS");
|
||||
E_LOG_DEBUG("Configuring INPUT FPS ...");
|
||||
E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
|
||||
}
|
||||
|
||||
if (_h264_encoder_init_buffers(enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
if (_m2m_encoder_init_buffers(enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
&enc->input_bufs, &enc->n_input_bufs, dma) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (_h264_encoder_init_buffers(enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
if (_m2m_encoder_init_buffers(enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
&enc->output_bufs, &enc->n_output_bufs, false) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
LOG_DEBUG("H264: Starting INPUT ...");
|
||||
ENCODER_XIOCTL(VIDIOC_STREAMON, &type, "H264: Can't start INPUT");
|
||||
E_LOG_DEBUG("Starting INPUT ...");
|
||||
E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start INPUT");
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
LOG_DEBUG("H264: Starting OUTPUT ...");
|
||||
ENCODER_XIOCTL(VIDIOC_STREAMON, &type, "H264: Can't start OUTPUT");
|
||||
E_LOG_DEBUG("Starting OUTPUT ...");
|
||||
E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
|
||||
}
|
||||
|
||||
enc->ready = true;
|
||||
LOG_DEBUG("H264: Encoder state: *** READY ***");
|
||||
E_LOG_DEBUG("Encoder state: *** READY ***");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_h264_encoder_cleanup(enc);
|
||||
LOG_ERROR("H264: Encoder destroyed due an error (prepare)");
|
||||
_m2m_encoder_cleanup(enc);
|
||||
E_LOG_ERROR("Encoder destroyed due an error (prepare)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _h264_encoder_init_buffers(
|
||||
h264_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
h264_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
|
||||
static int _m2m_encoder_init_buffers(
|
||||
m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
|
||||
m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
|
||||
|
||||
LOG_DEBUG("H264: Initializing %s buffers: ...", name);
|
||||
E_LOG_DEBUG("Initializing %s buffers ...", name);
|
||||
|
||||
struct v4l2_requestbuffers req = {0};
|
||||
req.count = 1;
|
||||
req.type = type;
|
||||
req.memory = (dma ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP);
|
||||
|
||||
LOG_DEBUG("H264: Requesting %u %s buffers ...", req.count, name);
|
||||
ENCODER_XIOCTL(VIDIOC_REQBUFS, &req, "H264: Can't request %s buffers", name);
|
||||
E_LOG_DEBUG("Requesting %u %s buffers ...", req.count, name);
|
||||
E_XIOCTL(VIDIOC_REQBUFS, &req, "Can't request %s buffers", name);
|
||||
if (req.count < 1) {
|
||||
LOG_ERROR("H264: Insufficient %s buffer memory: %u", name, req.count);
|
||||
E_LOG_ERROR("Insufficient %s buffer memory: %u", name, req.count);
|
||||
goto error;
|
||||
}
|
||||
LOG_DEBUG("H264: Got %u %s buffers", req.count, name);
|
||||
E_LOG_DEBUG("Got %u %s buffers", req.count, name);
|
||||
|
||||
if (dma) {
|
||||
*n_bufs_ptr = req.count;
|
||||
@@ -213,10 +208,10 @@ static int _h264_encoder_init_buffers(
|
||||
buf.length = 1;
|
||||
buf.m.planes = &plane;
|
||||
|
||||
LOG_DEBUG("H264: Querying %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
ENCODER_XIOCTL(VIDIOC_QUERYBUF, &buf, "H264: Can't query %s buffer index=%u", name, *n_bufs_ptr);
|
||||
E_LOG_DEBUG("Querying %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer index=%u", name, *n_bufs_ptr);
|
||||
|
||||
LOG_DEBUG("H264: Mapping %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
E_LOG_DEBUG("Mapping %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
if (((*bufs_ptr)[*n_bufs_ptr].data = mmap(
|
||||
NULL,
|
||||
plane.length,
|
||||
@@ -225,13 +220,13 @@ static int _h264_encoder_init_buffers(
|
||||
enc->fd,
|
||||
plane.m.mem_offset
|
||||
)) == MAP_FAILED) {
|
||||
LOG_PERROR("H264: Can't map %s buffer index=%u", name, *n_bufs_ptr);
|
||||
E_LOG_PERROR("Can't map %s buffer index=%u", name, *n_bufs_ptr);
|
||||
goto error;
|
||||
}
|
||||
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
|
||||
|
||||
LOG_DEBUG("H264: Queuing %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
ENCODER_XIOCTL(VIDIOC_QBUF, &buf, "H264: Can't queue %s buffer index=%u", name, *n_bufs_ptr);
|
||||
E_LOG_DEBUG("Queuing %s buffer index=%u ...", name, *n_bufs_ptr);
|
||||
E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer index=%u", name, *n_bufs_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,13 +235,13 @@ static int _h264_encoder_init_buffers(
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _h264_encoder_cleanup(h264_encoder_s *enc) {
|
||||
static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
|
||||
if (enc->ready) {
|
||||
# define STOP_STREAM(_name, _type) { \
|
||||
enum v4l2_buf_type _type_var = _type; \
|
||||
LOG_DEBUG("H264: Stopping %s ...", _name); \
|
||||
E_LOG_DEBUG("Stopping %s ...", _name); \
|
||||
if (xioctl(enc->fd, VIDIOC_STREAMOFF, &_type_var) < 0) { \
|
||||
LOG_PERROR("H264: Can't stop %s", _name); \
|
||||
E_LOG_PERROR("Can't stop %s", _name); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -261,7 +256,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *enc) {
|
||||
for (unsigned index = 0; index < enc->n_##_target##_bufs; ++index) { \
|
||||
if (enc->_target##_bufs[index].allocated > 0 && enc->_target##_bufs[index].data != MAP_FAILED) { \
|
||||
if (munmap(enc->_target##_bufs[index].data, enc->_target##_bufs[index].allocated) < 0) { \
|
||||
LOG_PERROR("H264: Can't unmap %s buffer index=%u", #_name, index); \
|
||||
E_LOG_PERROR("Can't unmap %s buffer index=%u", #_name, index); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
@@ -278,7 +273,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *enc) {
|
||||
|
||||
if (enc->fd >= 0) {
|
||||
if (close(enc->fd) < 0) {
|
||||
LOG_PERROR("H264: Can't close encoder device");
|
||||
E_LOG_PERROR("Can't close encoder device");
|
||||
}
|
||||
enc->fd = -1;
|
||||
}
|
||||
@@ -286,10 +281,10 @@ static void _h264_encoder_cleanup(h264_encoder_s *enc) {
|
||||
enc->last_online = -1;
|
||||
enc->ready = false;
|
||||
|
||||
LOG_DEBUG("H264: Encoder state: ~~~ NOT READY ~~~");
|
||||
E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
|
||||
}
|
||||
|
||||
int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_dma_fd, frame_s *dest, bool force_key) {
|
||||
int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, int src_dma_fd, frame_s *dest, bool force_key) {
|
||||
assert(enc->ready);
|
||||
assert(src->used > 0);
|
||||
assert(enc->width == src->width);
|
||||
@@ -304,37 +299,37 @@ int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_dma_f
|
||||
|
||||
frame_copy_meta(src, dest);
|
||||
dest->encode_begin_ts = get_now_monotonic();
|
||||
dest->format = V4L2_PIX_FMT_H264;
|
||||
dest->format = enc->output_format;
|
||||
dest->stride = 0;
|
||||
|
||||
force_key = (force_key || enc->last_online != src->online);
|
||||
|
||||
if (_h264_encoder_compress_raw(enc, src, src_dma_fd, dest, force_key) < 0) {
|
||||
_h264_encoder_cleanup(enc);
|
||||
LOG_ERROR("H264: Encoder destroyed due an error (compress)");
|
||||
if (_m2m_encoder_compress_raw(enc, src, src_dma_fd, dest, force_key) < 0) {
|
||||
_m2m_encoder_cleanup(enc);
|
||||
E_LOG_ERROR("Encoder destroyed due an error (compress)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest->encode_end_ts = get_now_monotonic();
|
||||
LOG_VERBOSE("H264: Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
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);
|
||||
|
||||
enc->last_online = src->online;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _h264_encoder_compress_raw(
|
||||
h264_encoder_s *enc, const frame_s *src, int src_dma_fd,
|
||||
static int _m2m_encoder_compress_raw(
|
||||
m2m_encoder_s *enc, const frame_s *src, int src_dma_fd,
|
||||
frame_s *dest, bool force_key) {
|
||||
|
||||
LOG_DEBUG("H264: Compressing new frame; force_key=%d ...", force_key);
|
||||
E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
|
||||
|
||||
if (force_key) {
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
|
||||
ctl.value = 1;
|
||||
LOG_DEBUG("H264: Forcing keyframe ...")
|
||||
ENCODER_XIOCTL(VIDIOC_S_CTRL, &ctl, "H264: Can't force keyframe");
|
||||
E_LOG_DEBUG("Forcing keyframe ...")
|
||||
E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
|
||||
}
|
||||
|
||||
struct v4l2_buffer input_buf = {0};
|
||||
@@ -349,18 +344,18 @@ static int _h264_encoder_compress_raw(
|
||||
input_buf.memory = V4L2_MEMORY_DMABUF;
|
||||
input_buf.field = V4L2_FIELD_NONE;
|
||||
input_plane.m.fd = src_dma_fd;
|
||||
LOG_DEBUG("H264: Using INPUT-DMA buffer index=%u", input_buf.index);
|
||||
E_LOG_DEBUG("Using INPUT-DMA buffer index=%u", input_buf.index);
|
||||
} else {
|
||||
assert(src_dma_fd < 0);
|
||||
input_buf.memory = V4L2_MEMORY_MMAP;
|
||||
LOG_DEBUG("H264: Grabbing INPUT buffer ...");
|
||||
ENCODER_XIOCTL(VIDIOC_DQBUF, &input_buf, "H264: Can't grab INPUT buffer");
|
||||
E_LOG_DEBUG("Grabbing INPUT buffer ...");
|
||||
E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
|
||||
if (input_buf.index >= enc->n_input_bufs) {
|
||||
LOG_ERROR("H264: V4L2 error: grabbed invalid INPUT buffer: index=%u, n_bufs=%u",
|
||||
E_LOG_ERROR("V4L2 error: grabbed invalid INPUT buffer: index=%u, n_bufs=%u",
|
||||
input_buf.index, enc->n_input_bufs);
|
||||
goto error;
|
||||
}
|
||||
LOG_DEBUG("H264: Grabbed INPUT buffer index=%u", input_buf.index);
|
||||
E_LOG_DEBUG("Grabbed INPUT buffer index=%u", input_buf.index);
|
||||
}
|
||||
|
||||
uint64_t now = get_now_monotonic_u64();
|
||||
@@ -374,22 +369,24 @@ static int _h264_encoder_compress_raw(
|
||||
|
||||
const char *input_name = (enc->dma ? "INPUT-DMA" : "INPUT");
|
||||
|
||||
LOG_DEBUG("H264: Sending %s buffer ...", input_name);
|
||||
ENCODER_XIOCTL(VIDIOC_QBUF, &input_buf, "H264: Can't send %s buffer", input_name);
|
||||
E_LOG_DEBUG("Sending%s %s buffer ...", (!enc->dma ? " (releasing)" : ""), input_name);
|
||||
E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
|
||||
|
||||
// Для не-DMA отправка буфера по факту являтся освобождением этого буфера
|
||||
bool input_released = !enc->dma;
|
||||
|
||||
bool input_released = false;
|
||||
while (true) {
|
||||
struct pollfd enc_poll = {enc->fd, POLLIN, 0};
|
||||
|
||||
if (poll(&enc_poll, 1, 200) < 0 && errno != EINTR) {
|
||||
LOG_PERROR("H264: Can't poll encoder");
|
||||
E_LOG_PERROR("Can't poll encoder");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (enc_poll.revents & POLLIN) {
|
||||
if (!input_released) {
|
||||
LOG_DEBUG("H264: Releasing %s buffer index=%u ...", input_name, input_buf.index);
|
||||
ENCODER_XIOCTL(VIDIOC_DQBUF, &input_buf, "H264: Can't release %s buffer index=%u",
|
||||
E_LOG_DEBUG("Releasing %s buffer index=%u ...", input_name, input_buf.index);
|
||||
E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't release %s buffer index=%u",
|
||||
input_name, input_buf.index);
|
||||
input_released = true;
|
||||
}
|
||||
@@ -400,14 +397,14 @@ static int _h264_encoder_compress_raw(
|
||||
output_buf.memory = V4L2_MEMORY_MMAP;
|
||||
output_buf.length = 1;
|
||||
output_buf.m.planes = &output_plane;
|
||||
LOG_DEBUG("H264: Fetching OUTPUT buffer ...");
|
||||
ENCODER_XIOCTL(VIDIOC_DQBUF, &output_buf, "H264: Can't fetch OUTPUT buffer");
|
||||
E_LOG_DEBUG("Fetching OUTPUT buffer ...");
|
||||
E_XIOCTL(VIDIOC_DQBUF, &output_buf, "Can't fetch OUTPUT buffer");
|
||||
|
||||
frame_set_data(dest, enc->output_bufs[output_buf.index].data, output_plane.bytesused);
|
||||
dest->key = output_buf.flags & V4L2_BUF_FLAG_KEYFRAME;
|
||||
|
||||
LOG_DEBUG("H264: Releasing OUTPUT buffer index=%u ...", output_buf.index);
|
||||
ENCODER_XIOCTL(VIDIOC_QBUF, &output_buf, "H264: Can't release OUTPUT buffer index=%u", output_buf.index);
|
||||
E_LOG_DEBUG("Releasing OUTPUT buffer index=%u ...", output_buf.index);
|
||||
E_XIOCTL(VIDIOC_QBUF, &output_buf, "Can't release OUTPUT buffer index=%u", output_buf.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -417,4 +414,10 @@ static int _h264_encoder_compress_raw(
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef ENCODER_XIOCTL
|
||||
#undef E_XIOCTL
|
||||
|
||||
#undef E_LOG_DEBUG
|
||||
#undef E_LOG_VERBOSE
|
||||
#undef E_LOG_INFO
|
||||
#undef E_LOG_PERROR
|
||||
#undef E_LOG_ERROR
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
@@ -34,28 +35,36 @@
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "../../libs/tools.h"
|
||||
#include "../../libs/logging.h"
|
||||
#include "../../libs/frame.h"
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/frame.h"
|
||||
|
||||
#include "../xioctl.h"
|
||||
#include "xioctl.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t allocated;
|
||||
} h264_buffer_s;
|
||||
} m2m_buffer_s;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
unsigned bitrate; // Kbit-per-sec
|
||||
unsigned gop; // Interval between keyframes
|
||||
unsigned fps;
|
||||
char *name;
|
||||
bool required;
|
||||
uint32_t id;
|
||||
int32_t value;
|
||||
} m2m_option_s;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *path;
|
||||
unsigned output_format;
|
||||
unsigned fps;
|
||||
m2m_option_s *options;
|
||||
|
||||
int fd;
|
||||
h264_buffer_s *input_bufs;
|
||||
m2m_buffer_s *input_bufs;
|
||||
unsigned n_input_bufs;
|
||||
h264_buffer_s *output_bufs;
|
||||
m2m_buffer_s *output_bufs;
|
||||
unsigned n_output_bufs;
|
||||
|
||||
int last_online;
|
||||
@@ -66,12 +75,12 @@ typedef struct {
|
||||
unsigned stride;
|
||||
bool dma;
|
||||
bool ready;
|
||||
} h264_encoder_s;
|
||||
} m2m_encoder_s;
|
||||
|
||||
|
||||
h264_encoder_s *h264_encoder_init(const char *path, unsigned bitrate, unsigned gop, unsigned fps);
|
||||
void h264_encoder_destroy(h264_encoder_s *enc);
|
||||
m2m_encoder_s *m2m_encoder_init(const char *name, const char *path, unsigned format, unsigned fps, m2m_option_s *options);
|
||||
void m2m_encoder_destroy(m2m_encoder_s *enc);
|
||||
|
||||
bool h264_encoder_is_prepared_for(h264_encoder_s *enc, const frame_s *frame, bool dma);
|
||||
int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool dma);
|
||||
int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_dma_fd, frame_s *dest, bool force_key);
|
||||
bool m2m_encoder_is_prepared_for(m2m_encoder_s *enc, const frame_s *frame, bool dma);
|
||||
int m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame, bool dma);
|
||||
int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, int src_dma_fd, frame_s *dest, bool force_key);
|
||||
Reference in New Issue
Block a user