Issue #83: Moved H.264 to h264_v4l2m2m

This commit is contained in:
Maxim Devaev
2021-11-13 21:54:15 +03:00
parent 281f4eb3a0
commit 78a12f7ed2
14 changed files with 345 additions and 417 deletions

View File

@@ -221,7 +221,8 @@ Timeout for lock. Default: 1.
.SS "H264 sink options"
.TP
Available only if \fBWITH_OMX\fR feature enabled.
.BR \-\-h264\-device\ \fI/dev/path
Path to V4L2 mem-to-mem encoder device. Default: /dev/video11.
.TP
.BR \-\-h264\-sink\ \fIname
Use the specified shared memory object to sink H264 frames encoded by MMAL. Default: disabled.

View File

@@ -26,6 +26,7 @@ _USTR_SRCS = $(shell ls \
ustreamer/data/*.c \
ustreamer/encoders/cpu/*.c \
ustreamer/encoders/hw/*.c \
ustreamer/h264/*.c \
)
_DUMP_LIBS = $(_COMMON_LIBS)
@@ -43,11 +44,10 @@ endef
ifneq ($(call optbool,$(WITH_OMX)),)
_USTR_LIBS += -lbcm_host -lvcos -lvcsm -lopenmaxil -lmmal -lmmal_core -lmmal_util -lmmal_vc_client -lmmal_components -L$(RPI_VC_LIBS)
_USTR_LIBS += -lbcm_host -lvcos -lvcsm -lopenmaxil -L$(RPI_VC_LIBS)
override _CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
_USTR_SRCS += $(shell ls \
ustreamer/encoders/omx/*.c \
ustreamer/h264/*.c \
)
endif

View File

@@ -178,16 +178,10 @@ void device_close(device_s *dev) {
for (unsigned index = 0; index < RUN(n_bufs); ++index) {
# define HW(_next) RUN(hw_bufs)[index]._next
# ifdef WITH_OMX
if (HW(vcsm_handle) > 0) {
vcsm_free(HW(vcsm_handle));
HW(vcsm_handle) = -1;
}
if (HW(dma_fd) >= 0) {
close(HW(dma_fd));
HW(dma_fd) = -1;
}
# endif
if (dev->io_method == V4L2_MEMORY_MMAP) {
if (HW(raw.allocated) > 0 && HW(raw.data) != MAP_FAILED) {
@@ -220,10 +214,8 @@ void device_close(device_s *dev) {
}
}
#ifdef WITH_OMX
int device_export_to_vcsm(device_s *dev) {
int device_export_to_dma(device_s *dev) {
# define DMA_FD RUN(hw_bufs[index].dma_fd)
# define VCSM_HANDLE RUN(hw_bufs[index].vcsm_handle)
for (unsigned index = 0; index < RUN(n_bufs); ++index) {
struct v4l2_exportbuffer exp = {0};
@@ -236,24 +228,12 @@ int device_export_to_vcsm(device_s *dev) {
goto error;
}
DMA_FD = exp.fd;
LOG_DEBUG("Importing DMA buffer fd=%d into VCSM ...", DMA_FD);
int vcsm_handle = vcsm_import_dmabuf(DMA_FD, "v4l2_buf");
if (vcsm_handle <= 0) {
LOG_PERROR("Unable to import DMA buffer fd=%d into VCSM", DMA_FD);
goto error;
}
VCSM_HANDLE = vcsm_handle;
}
return 0;
error:
for (unsigned index = 0; index < RUN(n_bufs); ++index) {
if (VCSM_HANDLE > 0) {
vcsm_free(VCSM_HANDLE);
VCSM_HANDLE = -1;
}
if (DMA_FD >= 0) {
close(DMA_FD);
DMA_FD = -1;
@@ -261,10 +241,8 @@ int device_export_to_vcsm(device_s *dev) {
}
return -1;
# undef VCSM_HANDLE
# undef DMA_FD
}
#endif
int device_switch_capturing(device_s *dev, bool enable) {
if (enable != RUN(capturing)) {
@@ -709,10 +687,7 @@ static int _device_open_io_method_mmap(device_s *dev) {
# define HW(_next) RUN(hw_bufs)[RUN(n_bufs)]._next
# ifdef WITH_OMX
HW(dma_fd) = -1;
HW(vcsm_handle) = -1;
# endif
A_MUTEX_INIT(&HW(grabbed_mutex));

View File

@@ -40,9 +40,6 @@
#include <pthread.h>
#include <linux/videodev2.h>
#include <linux/v4l2-controls.h>
#ifdef WITH_OMX
# include <interface/vcsm/user-vcsm.h>
#endif
#include "../libs/tools.h"
#include "../libs/logging.h"
@@ -73,12 +70,8 @@
typedef struct {
frame_s raw;
struct v4l2_buffer buf;
# ifdef WITH_OMX
int dma_fd;
int vcsm_handle;
# endif
struct v4l2_buffer buf;
int dma_fd;
pthread_mutex_t grabbed_mutex;
bool grabbed;
@@ -159,9 +152,7 @@ int device_parse_io_method(const char *str);
int device_open(device_s *dev);
void device_close(device_s *dev);
#ifdef WITH_OMX
int device_export_to_vcsm(device_s *dev);
#endif
int device_export_to_dma(device_s *dev);
int device_switch_capturing(device_s *dev, bool enable);
int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_error);
int device_grab_buffer(device_s *dev, hw_buffer_s **hw);

View File

@@ -23,80 +23,54 @@
#include "encoder.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 void _h264_encoder_cleanup(h264_encoder_s *enc);
static int _h264_encoder_compress_raw(h264_encoder_s *enc, const frame_s *src, int src_vcsm_handle, frame_s *dest, bool force_key);
static void _mmal_callback(MMAL_WRAPPER_T *wrapper);
static const char *_mmal_error_to_string(MMAL_STATUS_T error);
static int _h264_encoder_compress_raw(
h264_encoder_s *enc, const frame_s *src, int src_dma_fd,
frame_s *dest, bool force_key);
#define LOG_ERROR_MMAL(_error, _msg, ...) { \
LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _mmal_error_to_string(_error)); \
}
h264_encoder_s *h264_encoder_init(unsigned bitrate, unsigned gop, unsigned fps) {
LOG_INFO("H264: Initializing MMAL encoder ...");
h264_encoder_s *h264_encoder_init(const char *path, unsigned bitrate, unsigned gop, unsigned fps) {
LOG_INFO("H264: Initializing encoder ...");
LOG_INFO("H264: Using bitrate: %u Kbps", bitrate);
LOG_INFO("H264: Using GOP: %u", gop);
h264_encoder_s *enc;
A_CALLOC(enc, 1);
assert(enc->path = strdup(path));
enc->bitrate = bitrate; // Kbps
enc->gop = gop; // Interval between keyframes
enc->fps = fps;
enc->last_online = -1;
if (vcos_semaphore_create(&enc->handler_sem, "h264_handler_sem", 0) != VCOS_SUCCESS) {
LOG_PERROR("H264: Can't create VCOS semaphore");
goto error;
}
enc->i_handler_sem = true;
MMAL_STATUS_T error = mmal_wrapper_create(&enc->wrapper, MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER);
if (error != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't create MMAL wrapper");
enc->wrapper = NULL;
goto error;
}
enc->wrapper->user_data = (void *)enc;
enc->wrapper->callback = _mmal_callback;
return enc;
error:
h264_encoder_destroy(enc);
return NULL;
}
void h264_encoder_destroy(h264_encoder_s *enc) {
LOG_INFO("H264: Destroying MMAL encoder ...");
LOG_INFO("H264: Destroying encoder ...");
_h264_encoder_cleanup(enc);
if (enc->wrapper) {
MMAL_STATUS_T error = mmal_wrapper_destroy(enc->wrapper);
if (error != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't destroy MMAL encoder");
}
}
if (enc->i_handler_sem) {
vcos_semaphore_delete(&enc->handler_sem);
}
free(enc->path);
free(enc);
}
bool h264_encoder_is_prepared_for(h264_encoder_s *enc, const frame_s *frame, bool zero_copy) {
bool h264_encoder_is_prepared_for(h264_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->zero_copy == zero_copy));
return (EQ(width) && EQ(height) && EQ(format) && EQ(stride) && (enc->dma == dma));
# undef EQ
}
int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_copy) {
LOG_INFO("H264: Configuring MMAL encoder: zero_copy=%d ...", zero_copy);
#define ENCODER_XIOCTL(_request, _value, _msg, ...) { \
if (xioctl(enc->fd, _request, _value) < 0) { \
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);
_h264_encoder_cleanup(enc);
@@ -104,121 +78,86 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_co
enc->height = frame->height;
enc->format = frame->format;
enc->stride = frame->stride;
enc->zero_copy = zero_copy;
enc->dma = dma;
if (align_size(frame->width, 32) != frame->width && frame_get_padding(frame) == 0) {
LOG_ERROR("H264: MMAL encoder can't handle unaligned width");
if ((enc->fd = open(enc->path, O_RDWR)) < 0) {
LOG_PERROR("H264: Can't open encoder device");
goto error;
}
MMAL_STATUS_T error;
# define PREPARE_PORT(_id) { \
enc->_id##_port = enc->wrapper->_id[0]; \
if (enc->_id##_port->is_enabled) { \
if ((error = mmal_wrapper_port_disable(enc->_id##_port)) != MMAL_SUCCESS) { \
LOG_ERROR_MMAL(error, "H264: Can't disable MMAL %s port while configuring", #_id); \
goto error; \
} \
} \
}
# define COMMIT_PORT(_id) { \
if ((error = mmal_port_format_commit(enc->_id##_port)) != MMAL_SUCCESS) { \
LOG_ERROR_MMAL(error, "H264: Can't commit MMAL %s port", #_id); \
goto error; \
} \
}
# define SET_PORT_PARAM(_id, _type, _key, _value) { \
if ((error = mmal_port_parameter_set_##_type(enc->_id##_port, MMAL_PARAMETER_##_key, _value)) != MMAL_SUCCESS) { \
LOG_ERROR_MMAL(error, "H264: Can't set %s for the %s port", #_key, #_id); \
goto error; \
} \
}
# define ENABLE_PORT(_id) { \
if ((error = mmal_wrapper_port_enable(enc->_id##_port, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE)) != MMAL_SUCCESS) { \
LOG_ERROR_MMAL(error, "H264: Can't enable MMAL %s port", #_id); \
goto error; \
} \
}
{
PREPARE_PORT(input);
# define IFMT(_next) enc->input_port->format->_next
IFMT(type) = MMAL_ES_TYPE_VIDEO;
switch (frame->format) {
case V4L2_PIX_FMT_YUYV: IFMT(encoding) = MMAL_ENCODING_YUYV; break;
case V4L2_PIX_FMT_UYVY: IFMT(encoding) = MMAL_ENCODING_UYVY; break;
case V4L2_PIX_FMT_RGB565: IFMT(encoding) = MMAL_ENCODING_RGB16; break;
case V4L2_PIX_FMT_RGB24: IFMT(encoding) = MMAL_ENCODING_RGB24; break;
default: assert(0 && "Unsupported pixelformat");
}
IFMT(es->video.width) = align_size(frame->width, 32);
IFMT(es->video.height) = align_size(frame->height, 16);
IFMT(es->video.crop.x) = 0;
IFMT(es->video.crop.y) = 0;
IFMT(es->video.crop.width) = frame->width;
IFMT(es->video.crop.height) = frame->height;
IFMT(flags) = MMAL_ES_FORMAT_FLAG_FRAMED;
enc->input_port->buffer_size = 1000 * 1000;
enc->input_port->buffer_num = enc->input_port->buffer_num_recommended * 4;
# undef IFMT
COMMIT_PORT(input);
SET_PORT_PARAM(input, boolean, ZERO_COPY, zero_copy);
}
{
PREPARE_PORT(output);
# define OFMT(_next) enc->output_port->format->_next
OFMT(type) = MMAL_ES_TYPE_VIDEO;
OFMT(encoding) = MMAL_ENCODING_H264;
OFMT(encoding_variant) = MMAL_ENCODING_VARIANT_H264_DEFAULT;
OFMT(bitrate) = enc->bitrate * 1000;
OFMT(es->video.frame_rate.num) = enc->fps;
OFMT(es->video.frame_rate.den) = 1;
enc->output_port->buffer_size = enc->output_port->buffer_size_recommended * 4;
enc->output_port->buffer_num = enc->output_port->buffer_num_recommended;
# undef OFMT
COMMIT_PORT(output);
{
MMAL_PARAMETER_VIDEO_PROFILE_T profile;
MEMSET_ZERO(profile);
profile.hdr.id = MMAL_PARAMETER_PROFILE;
profile.hdr.size = sizeof(profile);
// http://blog.mediacoderhq.com/h264-profiles-and-levels
profile.profile[0].profile = MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE;
profile.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // Supports 1080p
if ((error = mmal_port_parameter_set(enc->output_port, &profile.hdr)) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't set MMAL_PARAMETER_PROFILE for the output port");
goto error;
# define SET_OPTION(_cid, _value) { \
struct v4l2_control _ctl = {0}; \
_ctl.id = _cid; \
_ctl.value = _value; \
LOG_DEBUG("H264: Configuring option %s ...", #_cid); \
ENCODER_XIOCTL(VIDIOC_S_CTRL, &_ctl, "H264: Can't set option " #_cid); \
}
}
SET_PORT_PARAM(output, boolean, ZERO_COPY, MMAL_TRUE);
SET_PORT_PARAM(output, uint32, INTRAPERIOD, enc->gop);
SET_PORT_PARAM(output, uint32, NALUNITFORMAT, MMAL_VIDEO_NALUNITFORMAT_STARTCODES);
SET_PORT_PARAM(output, boolean, MINIMISE_FRAGMENTATION, MMAL_TRUE);
SET_PORT_PARAM(output, uint32, MB_ROWS_PER_SLICE, 0);
SET_PORT_PARAM(output, boolean, VIDEO_IMMUTABLE_INPUT, MMAL_TRUE);
SET_PORT_PARAM(output, boolean, VIDEO_DROPPABLE_PFRAMES, MMAL_FALSE);
SET_PORT_PARAM(output, boolean, VIDEO_ENCODE_INLINE_HEADER, MMAL_TRUE); // SPS/PPS: https://github.com/raspberrypi/userland/issues/443
SET_PORT_PARAM(output, uint32, VIDEO_BIT_RATE, enc->bitrate * 1000);
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_PEAK_RATE, enc->bitrate * 1000);
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MIN_QUANT, 16);
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_MAX_QUANT, 34);
// Этот параметр с этим значением фризит кодирование изображения из черно-белой консоли
// SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_FRAME_LIMIT_BITS, 1000000);
SET_PORT_PARAM(output, uint32, VIDEO_ENCODE_H264_AU_DELIMITERS, MMAL_FALSE);
SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE, enc->bitrate * 1000);
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, enc->gop);
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
SET_OPTION(V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, 1);
# undef SET_OPTION
}
ENABLE_PORT(input);
ENABLE_PORT(output);
{
struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix_mp.width = frame->width;
fmt.fmt.pix_mp.height = frame->height;
fmt.fmt.pix_mp.pixelformat = frame->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;
LOG_DEBUG("H264: Configuring input format ...");
ENCODER_XIOCTL(VIDIOC_S_FMT, &fmt, "H264: Can't set input format");
}
{
struct v4l2_format fmt = {0};
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.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");
}
{
struct v4l2_streamparm setfps = {0};
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");
}
if (_h264_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,
&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");
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
LOG_DEBUG("H264: Starting output ...");
ENCODER_XIOCTL(VIDIOC_STREAMON, &type, "H264: Can't start output");
}
enc->ready = true;
return 0;
@@ -227,45 +166,126 @@ int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_co
_h264_encoder_cleanup(enc);
LOG_ERROR("H264: Encoder destroyed due an error (prepare)");
return -1;
}
# undef ENABLE_PORT
# undef SET_PORT_PARAM
# undef COMMIT_PORT
# undef PREPARE_PORT
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) {
LOG_DEBUG("H264: 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);
if (req.count < 1) {
LOG_ERROR("H264: Insufficient %s buffer memory: %u", name, req.count);
goto error;
}
LOG_DEBUG("H264: Got %u %s buffers", req.count, name);
if (dma) {
*n_bufs_ptr = req.count;
} else {
A_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;
LOG_DEBUG("H264: Querying %s buffer %u ...", name, *n_bufs_ptr);
ENCODER_XIOCTL(VIDIOC_QUERYBUF, &buf, "H264: Can't query %s buffer %u", name, *n_bufs_ptr);
LOG_DEBUG("H264: 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,
enc->fd,
plane.m.mem_offset
)) == MAP_FAILED) {
LOG_PERROR("H264: Can't map %s buffer %u", name, *n_bufs_ptr);
goto error;
}
(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
LOG_DEBUG("H264: Queuing %s buffer %u ...", name, *n_bufs_ptr);
ENCODER_XIOCTL(VIDIOC_QBUF, &buf, "H264: Can't queue %s buffer %u", name, *n_bufs_ptr);
}
}
return 0;
error:
return -1;
}
static void _h264_encoder_cleanup(h264_encoder_s *enc) {
MMAL_STATUS_T error;
# define DISABLE_PORT(_id) { \
if (enc->_id##_port) { \
if ((error = mmal_wrapper_port_disable(enc->_id##_port)) != MMAL_SUCCESS) { \
LOG_ERROR_MMAL(error, "H264: Can't disable MMAL %s port", #_id); \
if (enc->ready) {
# define STOP_STREAM(_name, _type) { \
enum v4l2_buf_type _type_var = _type; \
LOG_DEBUG("H264: Stopping %s ...", _name); \
if (xioctl(enc->fd, VIDIOC_STREAMOFF, &_type_var) < 0) { \
LOG_PERROR("H264: Can't stop %s", _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(_target) { \
if (enc->_target##_bufs) { \
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 %u", #_target, index); \
} \
} \
enc->_id##_port = NULL; \
} \
free(enc->_target##_bufs); \
enc->_target##_bufs = NULL; \
} \
enc->n_##_target##_bufs = 0; \
}
DESTROY_BUFFERS(output);
DESTROY_BUFFERS(input);
# undef DESTROY_BUFFERS
if (enc->fd >= 0) {
if (close(enc->fd) < 0) {
LOG_PERROR("H264: Can't close encoder device");
}
DISABLE_PORT(input);
DISABLE_PORT(output);
# undef DISABLE_PORT
if (enc->wrapper) {
enc->wrapper->status = MMAL_SUCCESS; // Это реально надо?
enc->fd = -1;
}
enc->last_online = -1;
enc->ready = false;
}
int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_vcsm_handle, frame_s *dest, bool force_key) {
int h264_encoder_compress(h264_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);
assert(enc->height == src->height);
assert(enc->format == src->format);
assert(enc->stride == src->stride);
if (enc->dma) {
assert(src_dma_fd >= 0);
} else {
assert(src_dma_fd < 0);
}
frame_copy_meta(src, dest);
dest->encode_begin_ts = get_now_monotonic();
@@ -274,7 +294,7 @@ int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_vcsm_
force_key = (force_key || enc->last_online != src->online);
if (_h264_encoder_compress_raw(enc, src, src_vcsm_handle, dest, force_key) < 0) {
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)");
return -1;
@@ -288,108 +308,98 @@ int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_vcsm_
return 0;
}
static int _h264_encoder_compress_raw(h264_encoder_s *enc, const frame_s *src, int src_vcsm_handle, frame_s *dest, bool force_key) {
static int _h264_encoder_compress_raw(
h264_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);
MMAL_STATUS_T error;
if (force_key) {
if ((error = mmal_port_parameter_set_boolean(
enc->output_port,
MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME,
MMAL_TRUE
)) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't request keyframe");
return -1;
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");
}
struct v4l2_buffer input_buf = {0};
struct v4l2_plane input_plane = {0};
input_buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
input_buf.length = 1;
input_buf.m.planes = &input_plane;
if (enc->dma) {
assert(src_dma_fd >= 0);
input_buf.index = 0;
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 %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");
if (input_buf.index >= enc->n_input_bufs) {
LOG_ERROR("H264: 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 %u", input_buf.index);
}
uint64_t now = get_now_monotonic_u64();
input_buf.timestamp.tv_sec = now / 1000000;
input_buf.timestamp.tv_usec = now % 1000000;
input_plane.bytesused = src->used;
input_plane.length = src->used;
if (!enc->dma) {
memcpy(enc->input_bufs[input_buf.index].data, src->data, src->used);
}
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);
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");
goto error;
}
if (enc_poll.revents & POLLIN) {
if (!input_released) {
LOG_DEBUG("H264: Releasing %s buffer %u ...", input_name, input_buf.index);
ENCODER_XIOCTL(VIDIOC_DQBUF, &input_buf, "H264: Can't release %s buffer %u",
input_name, input_buf.index);
input_released = true;
}
struct v4l2_buffer output_buf = {0};
struct v4l2_plane output_plane = {0};
output_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
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");
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 %u ...", output_buf.index);
ENCODER_XIOCTL(VIDIOC_QBUF, &output_buf, "H264: Can't release output buffer %u", output_buf.index);
break;
}
}
MMAL_BUFFER_HEADER_T *out = NULL;
MMAL_BUFFER_HEADER_T *in = NULL;
bool eos = false;
bool sent = false;
dest->used = 0;
while (!eos) {
out = NULL;
while (mmal_wrapper_buffer_get_empty(enc->output_port, &out, 0) == MMAL_SUCCESS) {
if ((error = mmal_port_send_buffer(enc->output_port, out)) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't send MMAL output buffer");
return -1;
}
}
in = NULL;
if (!sent && mmal_wrapper_buffer_get_empty(enc->input_port, &in, 0) == MMAL_SUCCESS) {
if (enc->zero_copy && src_vcsm_handle > 0) {
in->data = (uint8_t *)vcsm_vc_hdl_from_hdl(src_vcsm_handle);
} else {
in->data = src->data;
}
in->alloc_size = src->used;
in->length = src->used;
in->offset = 0;
in->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
if ((error = mmal_port_send_buffer(enc->input_port, in)) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't send MMAL input buffer");
return -1;
}
sent = true;
}
error = mmal_wrapper_buffer_get_full(enc->output_port, &out, 0);
if (error == MMAL_EAGAIN) {
if (vcos_my_semwait("H264: ", &enc->handler_sem, 1) < 0) {
return -1;
}
continue;
} else if (error != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't get MMAL output buffer");
return -1;
}
frame_append_data(dest, out->data, out->length);
dest->key = out->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
mmal_buffer_header_release(out);
}
if ((error = mmal_port_flush(enc->output_port)) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "H264: Can't flush MMAL output buffer; ignored");
}
return 0;
error:
return -1;
}
static void _mmal_callback(MMAL_WRAPPER_T *wrapper) {
vcos_semaphore_post(&((h264_encoder_s *)(wrapper->user_data))->handler_sem);
}
static const char *_mmal_error_to_string(MMAL_STATUS_T error) {
// http://www.jvcref.com/files/PI/documentation/html/group___mmal_types.html
# define CASE_ERROR(_name, _msg) case MMAL_##_name: return "MMAL_" #_name " [" _msg "]"
switch (error) {
case MMAL_SUCCESS: return "MMAL_SUCCESS";
CASE_ERROR(ENOMEM, "Out of memory");
CASE_ERROR(ENOSPC, "Out of resources");
CASE_ERROR(EINVAL, "Invalid argument");
CASE_ERROR(ENOSYS, "Function not implemented");
CASE_ERROR(ENOENT, "No such file or directory");
CASE_ERROR(ENXIO, "No such device or address");
CASE_ERROR(EIO, "IO error");
CASE_ERROR(ESPIPE, "Illegal seek");
CASE_ERROR(ECORRUPT, "Data is corrupt");
CASE_ERROR(ENOTREADY, "Component is not ready");
CASE_ERROR(ECONFIG, "Component is not configured");
CASE_ERROR(EISCONN, "Port is already connected");
CASE_ERROR(ENOTCONN, "Port is disconnected");
CASE_ERROR(EAGAIN, "Resource temporarily unavailable");
CASE_ERROR(EFAULT, "Bad address");
case MMAL_STATUS_MAX: break; // Makes cpplint happy
}
return "Unknown error";
# undef CASE_ERROR
}
#undef LOG_ERROR_MMAL
#undef ENCODER_XIOCTL

View File

@@ -24,33 +24,39 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <interface/mmal/mmal.h>
#include <interface/mmal/mmal_format.h>
#include <interface/mmal/util/mmal_default_components.h>
#include <interface/mmal/util/mmal_component_wrapper.h>
#include <interface/mmal/util/mmal_util_params.h>
#include <interface/vcsm/user-vcsm.h>
#include <linux/videodev2.h>
#include "../../libs/tools.h"
#include "../../libs/logging.h"
#include "../../libs/frame.h"
#include "../encoders/omx/vcos.h"
#include "../xioctl.h"
typedef struct {
unsigned bitrate; // Kbit-per-sec
unsigned gop; // Interval between keyframes
unsigned fps;
uint8_t *data;
size_t allocated;
} h264_buffer_s;
MMAL_WRAPPER_T *wrapper;
MMAL_PORT_T *input_port;
MMAL_PORT_T *output_port;
VCOS_SEMAPHORE_T handler_sem;
bool i_handler_sem;
typedef struct {
char *path;
unsigned bitrate; // Kbit-per-sec
unsigned gop; // Interval between keyframes
unsigned fps;
int fd;
h264_buffer_s *input_bufs;
unsigned n_input_bufs;
h264_buffer_s *output_bufs;
unsigned n_output_bufs;
int last_online;
@@ -58,14 +64,14 @@ typedef struct {
unsigned height;
unsigned format;
unsigned stride;
bool zero_copy;
bool dma;
bool ready;
} h264_encoder_s;
h264_encoder_s *h264_encoder_init(unsigned bitrate, unsigned gop, unsigned fps);
h264_encoder_s *h264_encoder_init(const char *path, unsigned bitrate, unsigned gop, unsigned fps);
void h264_encoder_destroy(h264_encoder_s *enc);
bool h264_encoder_is_prepared_for(h264_encoder_s *enc, const frame_s *frame, bool zero_copy);
int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame, bool zero_copy);
int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, int src_vcsm_handle, frame_s *dest, bool force_key);
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);

View File

@@ -23,7 +23,7 @@
#include "stream.h"
h264_stream_s *h264_stream_init(memsink_s *sink, unsigned bitrate, unsigned gop) {
h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop) {
h264_stream_s *h264;
A_CALLOC(h264, 1);
h264->sink = sink;
@@ -34,15 +34,9 @@ h264_stream_s *h264_stream_init(memsink_s *sink, unsigned bitrate, unsigned gop)
// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
// По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480
// енкодер через несколько секунд перестает производить корректные фреймы.
if ((h264->enc = h264_encoder_init(bitrate, gop, 30)) == NULL) {
goto error;
}
// TODO: Это было актуально для MMAL, надо проверить для V4L2.
h264->enc = h264_encoder_init(path, bitrate, gop, 30);
return h264;
error:
h264_stream_destroy(h264);
return NULL;
}
void h264_stream_destroy(h264_stream_s *h264) {
@@ -54,7 +48,7 @@ void h264_stream_destroy(h264_stream_s *h264) {
free(h264);
}
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int vcsm_handle, bool force_key) {
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int dma_fd, bool force_key) {
if (!memsink_server_check(h264->sink, frame)) {
return;
}
@@ -63,14 +57,14 @@ void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int vcsm_han
bool zero_copy = false;
if (is_jpeg(frame->format)) {
assert(vcsm_handle <= 0);
assert(dma_fd <= 0);
LOG_DEBUG("H264: Input frame is JPEG; decoding ...");
if (unjpeg(frame, h264->tmp_src, true) < 0) {
return;
}
frame = h264->tmp_src;
LOG_VERBOSE("H264: JPEG decoded; time=%.3Lf", get_now_monotonic() - now);
} else if (vcsm_handle > 0) {
} else if (dma_fd > 0) {
LOG_DEBUG("H264: Zero-copy available for the input");
zero_copy = true;
} else {
@@ -87,7 +81,7 @@ void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int vcsm_han
}
if (h264->enc->ready) {
if (h264_encoder_compress(h264->enc, frame, vcsm_handle, h264->dest, force_key) == 0) {
if (h264_encoder_compress(h264->enc, frame, dma_fd, h264->dest, force_key) == 0) {
online = !memsink_server_put(h264->sink, h264->dest);
}
}

View File

@@ -44,6 +44,6 @@ typedef struct {
} h264_stream_s;
h264_stream_s *h264_stream_init(memsink_s *sink, unsigned bitrate, unsigned gop);
h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop);
void h264_stream_destroy(h264_stream_s *h264);
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int vcsm_handle, bool force_key);
void h264_stream_process(h264_stream_s *h264, const frame_s *frame, int dma_fd, bool force_key);

View File

@@ -393,7 +393,6 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
enc_quality
));
# ifdef WITH_OMX
if (STREAM(run->h264)) {
assert(evbuffer_add_printf(buf,
" \"h264\": {\"bitrate\": %u, \"gop\": %u, \"online\": %s},",
@@ -402,14 +401,8 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
bool_to_string(atomic_load(&STREAM(run->h264->online)))
));
}
# endif
if (
STREAM(sink)
# ifdef WITH_OMX
|| STREAM(h264_sink)
# endif
) {
if (STREAM(sink) || STREAM(h264_sink)) {
assert(evbuffer_add_printf(buf, " \"sinks\": {"));
if (STREAM(sink)) {
assert(evbuffer_add_printf(buf,
@@ -417,7 +410,6 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
bool_to_string(atomic_load(&STREAM(sink->has_clients)))
));
}
# ifdef WITH_OMX
if (STREAM(h264_sink)) {
assert(evbuffer_add_printf(buf,
"%s\"h264\": {\"has_clients\": %s}",
@@ -425,7 +417,6 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
bool_to_string(atomic_load(&STREAM(h264_sink->has_clients)))
));
}
# endif
assert(evbuffer_add_printf(buf, "},"));
}

View File

@@ -131,11 +131,9 @@ int main(int argc, char *argv[]) {
if ((exit_code = options_parse(options, dev, enc, stream, server)) == 0) {
# ifdef WITH_OMX
if (enc->type == ENCODER_TYPE_OMX || stream->h264_sink) {
if (enc->type == ENCODER_TYPE_OMX) {
bcm_host_init();
i_bcm_host = true;
}
if (enc->type == ENCODER_TYPE_OMX) {
if ((omx_error = OMX_Init()) != OMX_ErrorNone) {
LOG_ERROR_OMX(omx_error, "Can't initialize OMX Core; forced CPU encoder");
enc->type = ENCODER_TYPE_CPU;

View File

@@ -94,11 +94,10 @@ enum _OPT_VALUES {
_O_##_prefix##_TIMEOUT,
ADD_SINK(SINK)
ADD_SINK(RAW_SINK)
# ifdef WITH_OMX
ADD_SINK(H264_SINK)
_O_H264_DEVICE,
_O_H264_BITRATE,
_O_H264_GOP,
# endif
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -192,11 +191,10 @@ static const struct option _LONG_OPTS[] = {
{_opt "sink-timeout", required_argument, NULL, _O_##_prefix##_TIMEOUT},
ADD_SINK("", SINK)
ADD_SINK("raw-", RAW_SINK)
# ifdef WITH_OMX
ADD_SINK("h264-", H264_SINK)
{"h264-device", required_argument, NULL, _O_H264_DEVICE},
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
{"h264-gop", required_argument, NULL, _O_H264_GOP},
# endif
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -258,9 +256,7 @@ void options_destroy(options_s *options) {
}
ADD_SINK(sink);
ADD_SINK(raw_sink);
# ifdef WITH_OMX
ADD_SINK(h264_sink);
# endif
# undef ADD_SINK
if (options->blank) {
@@ -353,9 +349,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
unsigned _prefix##_timeout = 1;
ADD_SINK(sink);
ADD_SINK(raw_sink);
# ifdef WITH_OMX
ADD_SINK(h264_sink);
# endif
# undef ADD_SINK
# ifdef WITH_SETPROCTITLE
@@ -447,11 +441,10 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
case _O_##_up##_TIMEOUT: OPT_NUMBER("--" #_opt "sink-timeout", _lp##_timeout, 1, 60, 0);
ADD_SINK("", sink, SINK)
ADD_SINK("raw-", raw_sink, RAW_SINK)
# ifdef WITH_OMX
ADD_SINK("h264-", h264_sink, H264_SINK)
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 25000, 0);
case _O_H264_DEVICE: OPT_SET(stream->h264_path, optarg);
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 100, 16000, 0);
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
# endif
# undef ADD_SINK
# ifdef WITH_GPIO
@@ -510,9 +503,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
}
ADD_SINK("JPEG", sink);
ADD_SINK("RAW", raw_sink);
# ifdef WITH_OMX
ADD_SINK("H264", h264_sink);
# endif
# undef ADD_SINK
# ifdef WITH_SETPROCTITLE
@@ -696,11 +687,10 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
SAY(" --" _opt "sink-timeout <sec> ──── Timeout for lock. Default: 1.\n");
ADD_SINK("JPEG", "")
ADD_SINK("RAW", "raw-")
# ifdef WITH_OMX
ADD_SINK("H264", "h264-")
SAY(" --h264-device </dev/path> ──── Path to V4L2 H.264 encoder device. Default: %s.\n", stream->h264_path);
SAY(" --h264-bitrate <kbps> ──────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
SAY(" --h264-gop <N> ─────────────── Intarval between keyframes. Default: %u.\n", stream->h264_gop);
# endif
# undef ADD_SINK
# ifdef WITH_GPIO
SAY("GPIO options:");

View File

@@ -56,9 +56,7 @@ typedef struct {
frame_s *blank;
memsink_s *sink;
memsink_s *raw_sink;
# ifdef WITH_OMX
memsink_s *h264_sink;
# endif
} options_s;

View File

@@ -36,13 +36,11 @@ static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
} \
}
#ifdef WITH_OMX
# define H264_PUT(_frame, _vcsm_handle, _force_key) { \
if (RUN(h264)) { \
h264_stream_process(RUN(h264), _frame, _vcsm_handle, _force_key); \
} \
}
#endif
#define H264_PUT(_frame, _dma_fd, _force_key) { \
if (RUN(h264)) { \
h264_stream_process(RUN(h264), _frame, _dma_fd, _force_key); \
} \
}
stream_s *stream_init(device_s *dev, encoder_s *enc) {
@@ -64,10 +62,9 @@ stream_s *stream_init(device_s *dev, encoder_s *enc) {
stream->enc = enc;
stream->last_as_blank = -1;
stream->error_delay = 1;
# ifdef WITH_OMX
stream->h264_path = "/dev/video11";
stream->h264_bitrate = 5000; // Kbps
stream->h264_gop = 30;
# endif
stream->run = run;
return stream;
}
@@ -86,11 +83,9 @@ void stream_loop(stream_s *stream) {
LOG_INFO("Using V4L2 device: %s", stream->dev->path);
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
# ifdef WITH_OMX
if (stream->h264_sink) {
RUN(h264) = h264_stream_init(stream->h264_sink, stream->h264_bitrate, stream->h264_gop);
RUN(h264) = h264_stream_init(stream->h264_sink, stream->h264_path, stream->h264_bitrate, stream->h264_gop);
}
# endif
for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
long double grab_after = 0;
@@ -126,18 +121,14 @@ void stream_loop(stream_s *stream) {
}
}
# ifdef WITH_OMX
bool h264_force_key = false;
# endif
if (stream->slowdown) {
unsigned slc = 0;
for (; slc < 10 && !atomic_load(&RUN(stop)) && !stream_has_clients(stream); ++slc) {
usleep(100000);
++slc;
}
# ifdef WITH_OMX
h264_force_key = (slc == 10);
# endif
}
if (atomic_load(&RUN(stop))) {
@@ -200,9 +191,7 @@ void stream_loop(stream_s *stream) {
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name);
SINK_PUT(raw_sink, &hw->raw);
# ifdef WITH_OMX
H264_PUT(&hw->raw, hw->vcsm_handle, h264_force_key);
# endif
H264_PUT(&hw->raw, hw->dma_fd, h264_force_key);
}
} else if (buf_index != -2) { // -2 for broken frame
break;
@@ -232,11 +221,9 @@ void stream_loop(stream_s *stream) {
# endif
}
# ifdef WITH_OMX
if (RUN(h264)) {
h264_stream_destroy(RUN(h264));
}
# endif
}
void stream_loop_break(stream_s *stream) {
@@ -248,9 +235,7 @@ bool stream_has_clients(stream_s *stream) {
atomic_load(&RUN(video->has_clients))
// has_clients синков НЕ обновляются в реальном времени
|| (stream->sink != NULL && atomic_load(&stream->sink->has_clients))
# ifdef WITH_OMX
|| (RUN(h264) != NULL && /*RUN(h264->sink) == NULL ||*/ atomic_load(&RUN(h264->sink->has_clients)))
# endif
);
}
@@ -292,11 +277,9 @@ static workers_pool_s *_stream_init_one(stream_s *stream) {
if (device_open(stream->dev) < 0) {
goto error;
}
# ifdef WITH_OMX
if (RUN(h264) && !is_jpeg(stream->dev->run->format)) {
device_export_to_vcsm(stream->dev);
device_export_to_dma(stream->dev);
}
# endif
if (device_switch_capturing(stream->dev, true) < 0) {
goto error;
}
@@ -364,16 +347,12 @@ static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
if (frame == NULL) {
SINK_PUT(raw_sink, stream->blank);
# ifdef WITH_OMX
H264_PUT(stream->blank, -1, false);
# endif
}
# undef VID
}
#ifdef WITH_OMX
# undef H264_PUT
#endif
#undef H264_PUT
#undef SINK_PUT
#undef RUN

View File

@@ -42,9 +42,7 @@
#include "device.h"
#include "encoder.h"
#include "workers.h"
#ifdef WITH_OMX
# include "h264/stream.h"
#endif
#include "h264/stream.h"
#ifdef WITH_GPIO
# include "gpio/gpio.h"
#endif
@@ -63,9 +61,7 @@ typedef struct {
video_s *video;
long double last_as_blank_ts;
# ifdef WITH_OMX
h264_stream_s *h264;
# endif
atomic_bool stop;
} stream_runtime_s;
@@ -82,11 +78,10 @@ typedef struct {
memsink_s *sink;
memsink_s *raw_sink;
# ifdef WITH_OMX
memsink_s *h264_sink;
char *h264_path;
unsigned h264_bitrate;
unsigned h264_gop;
# endif
stream_runtime_s *run;
} stream_s;