diff --git a/man/ustreamer.1 b/man/ustreamer.1 index 8631a1b..09d5b9d 100644 --- a/man/ustreamer.1 +++ b/man/ustreamer.1 @@ -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. diff --git a/src/Makefile b/src/Makefile index c311e2e..295b5c6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/ustreamer/device.c b/src/ustreamer/device.c index 79f87f8..4ee91d3 100644 --- a/src/ustreamer/device.c +++ b/src/ustreamer/device.c @@ -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)); diff --git a/src/ustreamer/device.h b/src/ustreamer/device.h index 4fad669..dfc6c40 100644 --- a/src/ustreamer/device.h +++ b/src/ustreamer/device.h @@ -40,9 +40,6 @@ #include #include #include -#ifdef WITH_OMX -# include -#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); diff --git a/src/ustreamer/h264/encoder.c b/src/ustreamer/h264/encoder.c index a49bc07..5702434 100644 --- a/src/ustreamer/h264/encoder.c +++ b/src/ustreamer/h264/encoder.c @@ -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 diff --git a/src/ustreamer/h264/encoder.h b/src/ustreamer/h264/encoder.h index 01c786c..edc1520 100644 --- a/src/ustreamer/h264/encoder.h +++ b/src/ustreamer/h264/encoder.h @@ -24,33 +24,39 @@ #include #include +#include +#include +#include +#include #include -#include +#include -#include -#include -#include -#include -#include -#include +#include #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); diff --git a/src/ustreamer/h264/stream.c b/src/ustreamer/h264/stream.c index 1d1e3d4..80186bb 100644 --- a/src/ustreamer/h264/stream.c +++ b/src/ustreamer/h264/stream.c @@ -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); } } diff --git a/src/ustreamer/h264/stream.h b/src/ustreamer/h264/stream.h index f752b28..dd1eb3b 100644 --- a/src/ustreamer/h264/stream.h +++ b/src/ustreamer/h264/stream.h @@ -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); diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c index 32f0b43..bf62cd0 100644 --- a/src/ustreamer/http/server.c +++ b/src/ustreamer/http/server.c @@ -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, "},")); } diff --git a/src/ustreamer/main.c b/src/ustreamer/main.c index 575b5a6..a2b8d00 100644 --- a/src/ustreamer/main.c +++ b/src/ustreamer/main.c @@ -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; diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c index 7df0079..97b7ef3 100644 --- a/src/ustreamer/options.c +++ b/src/ustreamer/options.c @@ -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 ──── Timeout for lock. Default: 1.\n"); ADD_SINK("JPEG", "") ADD_SINK("RAW", "raw-") -# ifdef WITH_OMX ADD_SINK("H264", "h264-") + SAY(" --h264-device ──── Path to V4L2 H.264 encoder device. Default: %s.\n", stream->h264_path); SAY(" --h264-bitrate ──────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate); SAY(" --h264-gop ─────────────── Intarval between keyframes. Default: %u.\n", stream->h264_gop); -# endif # undef ADD_SINK # ifdef WITH_GPIO SAY("GPIO options:"); diff --git a/src/ustreamer/options.h b/src/ustreamer/options.h index 0610b79..6f62214 100644 --- a/src/ustreamer/options.h +++ b/src/ustreamer/options.h @@ -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; diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c index 2a0136b..213c2d8 100644 --- a/src/ustreamer/stream.c +++ b/src/ustreamer/stream.c @@ -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 diff --git a/src/ustreamer/stream.h b/src/ustreamer/stream.h index 58ec503..a1e8e5d 100644 --- a/src/ustreamer/stream.h +++ b/src/ustreamer/stream.h @@ -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;