From 2630147a965e28ca43516c6e9ed695b46694bbe3 Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Tue, 5 Jan 2021 21:26:07 +0300 Subject: [PATCH] aligned input --- src/libs/common/frame.c | 30 +++++++++-- src/libs/common/frame.h | 26 ++++++--- src/libs/common/unjpeg.c | 23 ++++---- src/libs/memsink/memsink.c | 2 + src/libs/memsink/memsink.h | 1 + src/ustreamer/device.c | 20 +++++-- src/ustreamer/device.h | 1 + src/ustreamer/encoder.c | 23 +++++--- src/ustreamer/encoders/cpu/encoder.c | 27 ++++++---- src/ustreamer/encoders/omx/encoder.c | 81 +++++++++++++--------------- src/ustreamer/encoders/omx/encoder.h | 2 +- src/ustreamer/h264/encoder.c | 28 ++++++---- src/ustreamer/h264/encoder.h | 3 +- src/ustreamer/stream.c | 2 +- 14 files changed, 167 insertions(+), 102 deletions(-) diff --git a/src/libs/common/frame.c b/src/libs/common/frame.c index ee1d747..5d7b4bc 100644 --- a/src/libs/common/frame.c +++ b/src/libs/common/frame.c @@ -79,6 +79,7 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) { COPY(width); COPY(height); COPY(format); + COPY(stride); COPY(online); COPY(grab_ts); COPY(encode_begin_ts); @@ -88,14 +89,35 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) { #undef COPY bool frame_compare(const frame_s *a, const frame_s *b) { +# define CMP(_field) (a->_field == b->_field) return ( a->allocated && b->allocated - && a->used == b->used - && a->width == b->width - && a->height == b->height - && a->online == b->online + && CMP(used) + && CMP(width) + && CMP(height) + && CMP(format) + && CMP(stride) + && CMP(online) && !memcmp(a->data, b->data, b->used) ); +# undef CMP +} + +unsigned frame_get_padding(const frame_s *frame) { + unsigned bytes_per_pixel = 0; + switch (frame->format) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_RGB565: bytes_per_pixel = 2; break; + case V4L2_PIX_FMT_RGB24: bytes_per_pixel = 3; break; + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_JPEG: bytes_per_pixel = 0; break; + default: assert(0 && "Unknown pixelformat"); + } + if (bytes_per_pixel > 0 && frame->stride > frame->width) { + return (frame->stride - frame->width * bytes_per_pixel); + } + return 0; } const char *fourcc_to_string(unsigned format, char *buf, size_t size) { diff --git a/src/libs/common/frame.h b/src/libs/common/frame.h index cc89429..a4f3020 100644 --- a/src/libs/common/frame.h +++ b/src/libs/common/frame.h @@ -29,20 +29,28 @@ #include #include +#include + #include "tools.h" #include "logging.h" typedef struct { - const char *name; + const char *name; - uint8_t *data; - size_t used; - size_t allocated; - unsigned width; - unsigned height; - unsigned format; - bool online; + uint8_t *data; + size_t used; + size_t allocated; + + unsigned width; + unsigned height; + unsigned format; + unsigned stride; + // Stride is a bytesperline in V4L2 + // https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/pixfmt-v4l2.html + // https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd + + bool online; long double grab_ts; long double encode_begin_ts; @@ -63,4 +71,6 @@ void frame_copy(const frame_s *src, frame_s *dest); void frame_copy_meta(const frame_s *src, frame_s *dest); bool frame_compare(const frame_s *a, const frame_s *b); +unsigned frame_get_padding(const frame_s *frame); + const char *fourcc_to_string(unsigned format, char *buf, size_t size); diff --git a/src/libs/common/unjpeg.c b/src/libs/common/unjpeg.c index ba5ffde..716e1e2 100644 --- a/src/libs/common/unjpeg.c +++ b/src/libs/common/unjpeg.c @@ -39,12 +39,7 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) { struct jpeg_decompress_struct jpeg; jpeg_create_decompress(&jpeg); - frame_copy_meta(src, dest); - dest->format = V4L2_PIX_FMT_RGB24; - dest->used = 0; - // https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg - _jpeg_error_manager_s jpeg_error; jpeg.err = jpeg_std_error((struct jpeg_error_mgr *)&jpeg_error); jpeg_error.mgr.error_exit = _jpeg_error_handler; @@ -60,24 +55,26 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) { jpeg_start_decompress(&jpeg); + frame_copy_meta(src, dest); + dest->format = V4L2_PIX_FMT_RGB24; + dest->width = jpeg.output_width; + dest->height = jpeg.output_height; + dest->stride = jpeg.output_width * jpeg.output_components; // Row stride + dest->used = 0; + if (decode) { - const unsigned row_stride = jpeg.output_width * jpeg.output_components; - JSAMPARRAY scanlines; - scanlines = (*jpeg.mem->alloc_sarray)((j_common_ptr) &jpeg, JPOOL_IMAGE, row_stride, 1); + scanlines = (*jpeg.mem->alloc_sarray)((j_common_ptr) &jpeg, JPOOL_IMAGE, dest->stride, 1); - frame_realloc_data(dest, ((src->width * src->height) << 1) * 2); + frame_realloc_data(dest, ((dest->width * dest->height) << 1) * 2); while (jpeg.output_scanline < jpeg.output_height) { jpeg_read_scanlines(&jpeg, scanlines, 1); - frame_append_data(dest, scanlines[0], row_stride); + frame_append_data(dest, scanlines[0], dest->stride); } jpeg_finish_decompress(&jpeg); } - dest->width = jpeg.output_width; - dest->height = jpeg.output_height; - done: jpeg_destroy_decompress(&jpeg); return retval; diff --git a/src/libs/memsink/memsink.c b/src/libs/memsink/memsink.c index d3aae88..d09fcac 100644 --- a/src/libs/memsink/memsink.c +++ b/src/libs/memsink/memsink.c @@ -153,6 +153,7 @@ int memsink_server_put(memsink_s *sink, const frame_s *frame) { COPY(width); COPY(height); COPY(format); + COPY(stride); COPY(online); COPY(grab_ts); COPY(encode_begin_ts); @@ -203,6 +204,7 @@ int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress u COPY(width); COPY(height); COPY(format); + COPY(stride); COPY(online); COPY(grab_ts); COPY(encode_begin_ts); diff --git a/src/libs/memsink/memsink.h b/src/libs/memsink/memsink.h index ceffc62..5142ce5 100644 --- a/src/libs/memsink/memsink.h +++ b/src/libs/memsink/memsink.h @@ -51,6 +51,7 @@ typedef struct { unsigned width; unsigned height; unsigned format; + unsigned stride; bool online; long double grab_ts; long double encode_begin_ts; diff --git a/src/ustreamer/device.c b/src/ustreamer/device.c index f636da2..680b800 100644 --- a/src/ustreamer/device.c +++ b/src/ustreamer/device.c @@ -57,7 +57,7 @@ static const struct { static int _device_open_check_cap(device_s *dev); static int _device_open_dv_timings(device_s *dev); static int _device_apply_dv_timings(device_s *dev); -static int _device_open_format(device_s *dev); +static int _device_open_format(device_s *dev, bool first); static void _device_open_hw_fps(device_s *dev); static void _device_open_jpeg_quality(device_s *dev); static int _device_open_io_method(device_s *dev); @@ -149,7 +149,7 @@ int device_open(device_s *dev) { if (_device_open_dv_timings(dev) < 0) { goto error; } - if (_device_open_format(dev) < 0) { + if (_device_open_format(dev, true) < 0) { goto error; } _device_open_hw_fps(dev); @@ -327,6 +327,7 @@ int device_grab_buffer(device_s *dev, hw_buffer_s **hw) { HW(raw.width) = RUN(width); HW(raw.height) = RUN(height); HW(raw.format) = RUN(format); + HW(raw.stride) = RUN(stride); HW(raw.online) = true; memcpy(&HW(buf_info), &buf_info, sizeof(struct v4l2_buffer)); HW(raw.grab_ts) = get_now_monotonic(); @@ -464,7 +465,9 @@ static int _device_apply_dv_timings(device_s *dev) { return 0; } -static int _device_open_format(device_s *dev) { +static int _device_open_format(device_s *dev, bool first) { + const unsigned stride = align_size(RUN(width), 32) << 1; + struct v4l2_format fmt; MEMSET_ZERO(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -472,22 +475,28 @@ static int _device_open_format(device_s *dev) { fmt.fmt.pix.height = RUN(height); fmt.fmt.pix.pixelformat = dev->format; fmt.fmt.pix.field = V4L2_FIELD_ANY; + fmt.fmt.pix.bytesperline = stride; // Set format LOG_DEBUG("Calling ioctl(VIDIOC_S_FMT) ..."); if (xioctl(RUN(fd), VIDIOC_S_FMT, &fmt) < 0) { - LOG_PERROR("Unable to set pixelformat=%s, resolution=%ux%u", - _format_to_string_supported(dev->format), RUN(width), RUN(height)); + LOG_PERROR("Unable to set pixelformat=%s, stride=%u, resolution=%ux%u", + _format_to_string_supported(dev->format), stride, RUN(width), RUN(height)); return -1; } // Check resolution + bool retry = false; if (fmt.fmt.pix.width != RUN(width) || fmt.fmt.pix.height != RUN(height)) { LOG_ERROR("Requested resolution=%ux%u is unavailable", RUN(width), RUN(height)); + retry = true; } if (_device_apply_resolution(dev, fmt.fmt.pix.width, fmt.fmt.pix.height) < 0) { return -1; } + if (first && retry) { + return _device_open_format(dev, false); + } LOG_INFO("Using resolution: %ux%u", RUN(width), RUN(height)); // Check format @@ -511,6 +520,7 @@ static int _device_open_format(device_s *dev) { RUN(format) = fmt.fmt.pix.pixelformat; LOG_INFO("Using pixelformat: %s", _format_to_string_supported(RUN(format))); + RUN(stride) = fmt.fmt.pix.bytesperline; RUN(raw_size) = fmt.fmt.pix.sizeimage; // Only for userptr return 0; } diff --git a/src/ustreamer/device.h b/src/ustreamer/device.h index f98c6f7..36def00 100644 --- a/src/ustreamer/device.h +++ b/src/ustreamer/device.h @@ -81,6 +81,7 @@ typedef struct { unsigned width; unsigned height; unsigned format; + unsigned stride; unsigned hw_fps; unsigned jpeg_quality; size_t raw_size; diff --git a/src/ustreamer/encoder.c b/src/ustreamer/encoder.c index d0eb0c8..c86553a 100644 --- a/src/ustreamer/encoder.c +++ b/src/ustreamer/encoder.c @@ -97,13 +97,13 @@ void encoder_prepare(encoder_s *enc, device_s *dev) { ER(n_workers) = min_u(enc->n_workers, DR(n_buffers)); if ((DR(format) == V4L2_PIX_FMT_MJPEG || DR(format) == V4L2_PIX_FMT_JPEG) && type != ENCODER_TYPE_HW) { - LOG_INFO("Switching to HW encoder because the input format is (M)JPEG"); + LOG_INFO("Switching to HW encoder: the input is (M)JPEG ..."); type = ENCODER_TYPE_HW; } if (type == ENCODER_TYPE_HW) { if (DR(format) != V4L2_PIX_FMT_MJPEG && DR(format) != V4L2_PIX_FMT_JPEG) { - LOG_INFO("Switching to CPU encoder because the input format is not (M)JPEG"); + LOG_INFO("Switching to CPU encoder: the input format is not (M)JPEG ..."); goto use_cpu; } quality = DR(jpeg_quality); @@ -111,8 +111,8 @@ void encoder_prepare(encoder_s *enc, device_s *dev) { } # ifdef WITH_OMX else if (type == ENCODER_TYPE_OMX) { - if (align_size(DR(width), 32) != DR(width) || align_size(DR(height), 16) != DR(height)) { - LOG_INFO("Switching to CPU encoder because OMX can't handle %ux%u", DR(width), DR(height)); + if (align_size(DR(width), 32) != DR(width)) { + LOG_INFO("Switching to CPU encoder: OMX can't handle width=%u ...", DR(width)); goto use_cpu; } @@ -135,9 +135,18 @@ void encoder_prepare(encoder_s *enc, device_s *dev) { } } + frame_s frame; + MEMSET_ZERO(frame); + frame.width = DR(width); + frame.height = DR(height); + frame.format = DR(format); + frame.stride = DR(stride); + for (unsigned index = 0; index < ER(n_omxs); ++index) { - if (omx_encoder_prepare(ER(omxs[index]), DR(width), DR(height), DR(format), quality) < 0) { - LOG_ERROR("Can't prepare OMX encoder, falling back to CPU"); + int omx_error = omx_encoder_prepare(ER(omxs[index]), &frame, quality); + if (omx_error == -2) { + goto use_cpu; + } else if (omx_error < 0) { goto force_cpu; } } @@ -154,6 +163,7 @@ void encoder_prepare(encoder_s *enc, device_s *dev) { # pragma GCC diagnostic push // cppcheck-suppress unusedLabel force_cpu: + LOG_ERROR("Forced CPU encoder permanently"); cpu_forced = true; # pragma GCC diagnostic pop @@ -196,6 +206,7 @@ int encoder_compress(encoder_s *enc, unsigned worker_number, frame_s *src, frame frame_copy_meta(src, dest); dest->format = V4L2_PIX_FMT_JPEG; + dest->stride = 0; dest->encode_begin_ts = get_now_monotonic(); dest->used = 0; diff --git a/src/ustreamer/encoders/cpu/encoder.c b/src/ustreamer/encoders/cpu/encoder.c index 3eb04e5..d79f673 100644 --- a/src/ustreamer/encoders/cpu/encoder.c +++ b/src/ustreamer/encoders/cpu/encoder.c @@ -113,6 +113,7 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const uint8_t *line_buffer; A_CALLOC(line_buffer, frame->width * 3); + const unsigned padding = frame_get_padding(frame); const uint8_t *data = frame->data; unsigned z = 0; @@ -137,9 +138,9 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const data += 4; } } + data += padding; - JSAMPROW scanlines[1]; - scanlines[0] = line_buffer; + JSAMPROW scanlines[1] = {line_buffer}; jpeg_write_scanlines(jpeg, scanlines, 1); } @@ -150,13 +151,14 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const uint8_t *line_buffer; A_CALLOC(line_buffer, frame->width * 3); + const unsigned padding = frame_get_padding(frame); const uint8_t *data = frame->data; unsigned z = 0; while (jpeg->next_scanline < frame->height) { uint8_t *ptr = line_buffer; - for(unsigned x = 0; x < frame->width; ++x) { + for (unsigned x = 0; x < frame->width; ++x) { int y = (!z ? data[1] << 8 : data[3] << 8); int u = data[0] - 128; int v = data[2] - 128; @@ -174,9 +176,9 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const data += 4; } } + data += padding; - JSAMPROW scanlines[1]; - scanlines[0] = line_buffer; + JSAMPROW scanlines[1] = {line_buffer}; jpeg_write_scanlines(jpeg, scanlines, 1); } @@ -192,12 +194,13 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons uint8_t *line_buffer; A_CALLOC(line_buffer, frame->width * 3); + const unsigned padding = frame_get_padding(frame); const uint8_t *data = frame->data; while (jpeg->next_scanline < frame->height) { uint8_t *ptr = line_buffer; - for(unsigned x = 0; x < frame->width; ++x) { + for (unsigned x = 0; x < frame->width; ++x) { unsigned int two_byte = (data[1] << 8) + data[0]; *(ptr++) = data[1] & 248; // Red @@ -206,9 +209,9 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons data += 2; } + data += padding; - JSAMPROW scanlines[1]; - scanlines[0] = line_buffer; + JSAMPROW scanlines[1] = {line_buffer}; jpeg_write_scanlines(jpeg, scanlines, 1); } @@ -216,10 +219,14 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons } static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame) { + const unsigned padding = frame_get_padding(frame); + uint8_t *data = frame->data; + while (jpeg->next_scanline < frame->height) { - JSAMPROW scanlines[1]; - scanlines[0] = (uint8_t *)(frame->data + jpeg->next_scanline * frame->width * 3); + JSAMPROW scanlines[1] = {data}; jpeg_write_scanlines(jpeg, scanlines, 1); + + data += (jpeg->next_scanline * frame->width * 3) + padding; } } diff --git a/src/ustreamer/encoders/omx/encoder.c b/src/ustreamer/encoders/omx/encoder.c index dad44b4..5965205 100644 --- a/src/ustreamer/encoders/omx/encoder.c +++ b/src/ustreamer/encoders/omx/encoder.c @@ -30,7 +30,7 @@ static const OMX_U32 _OUTPUT_PORT = 341; static int _vcos_semwait(VCOS_SEMAPHORE_T *sem); static int _omx_init_component(omx_encoder_s *omx); static int _omx_init_disable_ports(omx_encoder_s *omx); -static int _omx_setup_input(omx_encoder_s *omx, unsigned width, unsigned height, unsigned format); +static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame); static int _omx_setup_output(omx_encoder_s *omx, unsigned quality); static int _omx_encoder_clear_ports(omx_encoder_s *omx); @@ -106,14 +106,19 @@ void omx_encoder_destroy(omx_encoder_s *omx) { free(omx); } -int omx_encoder_prepare(omx_encoder_s *omx, unsigned width, unsigned height, unsigned format, unsigned quality) { +int omx_encoder_prepare(omx_encoder_s *omx, const frame_s *frame, unsigned quality) { + if (align_size(frame->width, 32) != frame->width && frame_get_padding(frame) == 0) { + LOG_ERROR("%u %u", frame->width, frame->stride); + LOG_ERROR("OMX encoder can't handle unaligned width"); + return -2; + } if (omx_component_set_state(&omx->comp, OMX_StateIdle) < 0) { return -1; } if (_omx_encoder_clear_ports(omx) < 0) { return -1; } - if (_omx_setup_input(omx, width, height, format) < 0) { + if (_omx_setup_input(omx, frame) < 0) { return -1; } if (_omx_setup_output(omx, quality) < 0) { @@ -136,7 +141,9 @@ int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest) return -1; } + dest->width = align_size(src->width, 32); dest->used = 0; + omx->output_available = false; omx->input_required = true; @@ -277,7 +284,7 @@ static int _omx_init_disable_ports(omx_encoder_s *omx) { return 0; } -static int _omx_setup_input(omx_encoder_s *omx, unsigned width, unsigned height, unsigned format) { +static int _omx_setup_input(omx_encoder_s *omx, const frame_s *frame) { LOG_DEBUG("Setting up OMX JPEG input port ..."); OMX_ERRORTYPE error; @@ -289,14 +296,14 @@ static int _omx_setup_input(omx_encoder_s *omx, unsigned width, unsigned height, } # define IFMT(_next) portdef.format.image._next - IFMT(nFrameWidth) = width; - IFMT(nFrameHeight) = height; - IFMT(nStride) = 0; - IFMT(nSliceHeight) = align_size(height, 16); + IFMT(nFrameWidth) = align_size(frame->width, 32); + IFMT(nFrameHeight) = frame->height; + IFMT(nStride) = align_size(frame->width, 32) << 1; + IFMT(nSliceHeight) = align_size(frame->height, 16); IFMT(bFlagErrorConcealment) = OMX_FALSE; IFMT(eCompressionFormat) = OMX_IMAGE_CodingUnused; - portdef.nBufferSize = ((width * height) << 1) * 2; - switch (format) { + portdef.nBufferSize = ((frame->width * frame->height) << 1) * 2; + switch (frame->format) { // https://www.fourcc.org/yuv.php // Also see comments inside OMX_IVCommon.h case V4L2_PIX_FMT_YUYV: IFMT(eColorFormat) = OMX_COLOR_FormatYCbYCr; break; @@ -307,7 +314,7 @@ static int _omx_setup_input(omx_encoder_s *omx, unsigned width, unsigned height, // FIXME: RGB24 не работает нормально, нижняя половина экрана зеленая. // FIXME: Китайский EasyCap тоже не работает, мусор на экране. // Вероятно обе проблемы вызваны некорректной реализацией OMX на пае. - default: assert(0 && "Unsupported input format for OMX JPEG encoder"); + default: assert(0 && "Unsupported pixelformat"); } # undef IFMT @@ -358,43 +365,31 @@ static int _omx_setup_output(omx_encoder_s *omx, unsigned quality) { return -1; } - { - OMX_CONFIG_BOOLEANTYPE exif; - - OMX_INIT_STRUCTURE(exif); - exif.bEnabled = OMX_FALSE; - - if ((error = OMX_SetParameter(omx->comp, OMX_IndexParamBrcmDisableEXIF, &exif)) != OMX_ErrorNone) { - LOG_ERROR_OMX(error, "Can't disable EXIF on OMX JPEG"); - return -1; +# define SET_PARAM(_key, _value) { \ + if ((error = OMX_SetParameter(omx->comp, OMX_IndexParam##_key, _value)) != OMX_ErrorNone) { \ + LOG_ERROR_OMX(error, "Can't set OMX param %s", #_key); \ + return -1; \ + } \ } - } - { - OMX_PARAM_IJGSCALINGTYPE ijg; + OMX_CONFIG_BOOLEANTYPE exif; + OMX_INIT_STRUCTURE(exif); + exif.bEnabled = OMX_FALSE; + SET_PARAM(BrcmDisableEXIF, &exif); - OMX_INIT_STRUCTURE(ijg); - ijg.nPortIndex = _OUTPUT_PORT; - ijg.bEnabled = OMX_TRUE; + OMX_PARAM_IJGSCALINGTYPE ijg; + OMX_INIT_STRUCTURE(ijg); + ijg.nPortIndex = _OUTPUT_PORT; + ijg.bEnabled = OMX_TRUE; + SET_PARAM(BrcmEnableIJGTableScaling, &ijg); - if ((error = OMX_SetParameter(omx->comp, OMX_IndexParamBrcmEnableIJGTableScaling, &ijg)) != OMX_ErrorNone) { - LOG_ERROR_OMX(error, "Can't set OMX JPEG IJG settings"); - return -1; - } - } + OMX_IMAGE_PARAM_QFACTORTYPE qfactor; + OMX_INIT_STRUCTURE(qfactor); + qfactor.nPortIndex = _OUTPUT_PORT; + qfactor.nQFactor = quality; + SET_PARAM(QFactor, &qfactor); - { - OMX_IMAGE_PARAM_QFACTORTYPE qfactor; - - OMX_INIT_STRUCTURE(qfactor); - qfactor.nPortIndex = _OUTPUT_PORT; - qfactor.nQFactor = quality; - - if ((error = OMX_SetParameter(omx->comp, OMX_IndexParamQFactor, &qfactor)) != OMX_ErrorNone) { - LOG_ERROR_OMX(error, "Can't set OMX JPEG quality"); - return -1; - } - } +# undef SET_PARAM if (omx_component_enable_port(&omx->comp, _OUTPUT_PORT) < 0) { return -1; diff --git a/src/ustreamer/encoders/omx/encoder.h b/src/ustreamer/encoders/omx/encoder.h index f3846bd..99457cd 100644 --- a/src/ustreamer/encoders/omx/encoder.h +++ b/src/ustreamer/encoders/omx/encoder.h @@ -68,5 +68,5 @@ typedef struct { omx_encoder_s *omx_encoder_init(void); void omx_encoder_destroy(omx_encoder_s *omx); -int omx_encoder_prepare(omx_encoder_s *omx, unsigned width, unsigned height, unsigned format, unsigned quality); +int omx_encoder_prepare(omx_encoder_s *omx, const frame_s *frame, unsigned quality); int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest); diff --git a/src/ustreamer/h264/encoder.c b/src/ustreamer/h264/encoder.c index 6c13910..a2aa589 100644 --- a/src/ustreamer/h264/encoder.c +++ b/src/ustreamer/h264/encoder.c @@ -95,9 +95,14 @@ void h264_encoder_destroy(h264_encoder_s *enc) { free(enc); } -int h264_encoder_prepare(h264_encoder_s *enc, unsigned width, unsigned height, unsigned format) { +int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame) { LOG_INFO("H264: Configuring MMAL encoder ..."); + if (align_size(frame->width, 32) != frame->width && frame_get_padding(frame) == 0) { + LOG_ERROR("H264: MMAL encoder can't handle unaligned width"); + goto error; + } + MMAL_STATUS_T error; # define PREPARE_PORT(_id) { \ @@ -139,21 +144,21 @@ int h264_encoder_prepare(h264_encoder_s *enc, unsigned width, unsigned height, u # define IFMT(_next) RUN(input_port->format->_next) IFMT(type) = MMAL_ES_TYPE_VIDEO; - switch (format) { + 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_MJPEG: case V4L2_PIX_FMT_JPEG: // See unjpeg.c case V4L2_PIX_FMT_RGB24: IFMT(encoding) = MMAL_ENCODING_RGB24; break; - default: assert(0 && "Unsupported input format for MMAL H264 encoder"); + default: assert(0 && "Unsupported pixelformat"); } - IFMT(es->video.width) = align_size(width, 32); - IFMT(es->video.height) = align_size(height, 16); + 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) = width; - IFMT(es->video.crop.height) = height; + IFMT(es->video.crop.width) = frame->width; + IFMT(es->video.crop.height) = frame->height; IFMT(flags) = MMAL_ES_FORMAT_FLAG_FRAMED; RUN(input_port->buffer_size) = 1000 * 1000; RUN(input_port->buffer_num) = RUN(input_port->buffer_num_recommended) * 4; @@ -211,9 +216,10 @@ int h264_encoder_prepare(h264_encoder_s *enc, unsigned width, unsigned height, u ENABLE_PORT(input); ENABLE_PORT(output); - RUN(width) = width; - RUN(height) = height; - RUN(format) = format; + RUN(width) = frame->width; + RUN(height) = frame->height; + RUN(format) = frame->format; + RUN(stride) = frame->stride; return 0; @@ -247,6 +253,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *enc) { RUN(width) = 0; RUN(height) = 0; RUN(format) = 0; + RUN(stride) = 0; RUN(last_online) = -1; } @@ -254,6 +261,7 @@ int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, frame_s *dest frame_copy_meta(src, dest); dest->encode_begin_ts = get_now_monotonic(); dest->format = V4L2_PIX_FMT_H264; + dest->stride = 0; if (src->format == V4L2_PIX_FMT_MJPEG || src->format == V4L2_PIX_FMT_JPEG) { LOG_DEBUG("H264: Input frame format is JPEG; decoding ..."); diff --git a/src/ustreamer/h264/encoder.h b/src/ustreamer/h264/encoder.h index 064b60d..6f9185b 100644 --- a/src/ustreamer/h264/encoder.h +++ b/src/ustreamer/h264/encoder.h @@ -53,6 +53,7 @@ typedef struct { unsigned width; unsigned height; unsigned format; + unsigned stride; } h264_encoder_runtime_s; typedef struct { @@ -67,5 +68,5 @@ typedef struct { h264_encoder_s *h264_encoder_init(void); void h264_encoder_destroy(h264_encoder_s *enc); -int h264_encoder_prepare(h264_encoder_s *enc, unsigned width, unsigned height, unsigned format); +int h264_encoder_prepare(h264_encoder_s *enc, const frame_s *frame); int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, frame_s *dest); diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c index 7970a63..e1675cc 100644 --- a/src/ustreamer/stream.c +++ b/src/ustreamer/stream.c @@ -431,7 +431,7 @@ static void _h264_stream_process(h264_stream_s *h264, const frame_s *frame) { # define NEQ(_field) (frame->_field != h264->enc->run->_field) if (NEQ(width) || NEQ(height) || NEQ(format)) { # undef NEQ - h264_encoder_prepare(h264->enc, frame->width, frame->height, frame->format); + h264_encoder_prepare(h264->enc, frame); } if (h264->enc->run->format) { if (h264_encoder_compress(h264->enc, frame, h264->dest) == 0) {