aligned input

This commit is contained in:
Devaev Maxim
2021-01-05 21:26:07 +03:00
parent 17bb7c77f3
commit 2630147a96
14 changed files with 167 additions and 102 deletions

View File

@@ -79,6 +79,7 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) {
COPY(width); COPY(width);
COPY(height); COPY(height);
COPY(format); COPY(format);
COPY(stride);
COPY(online); COPY(online);
COPY(grab_ts); COPY(grab_ts);
COPY(encode_begin_ts); COPY(encode_begin_ts);
@@ -88,14 +89,35 @@ void frame_copy_meta(const frame_s *src, frame_s *dest) {
#undef COPY #undef COPY
bool frame_compare(const frame_s *a, const frame_s *b) { bool frame_compare(const frame_s *a, const frame_s *b) {
# define CMP(_field) (a->_field == b->_field)
return ( return (
a->allocated && b->allocated a->allocated && b->allocated
&& a->used == b->used && CMP(used)
&& a->width == b->width && CMP(width)
&& a->height == b->height && CMP(height)
&& a->online == b->online && CMP(format)
&& CMP(stride)
&& CMP(online)
&& !memcmp(a->data, b->data, b->used) && !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) { const char *fourcc_to_string(unsigned format, char *buf, size_t size) {

View File

@@ -29,20 +29,28 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <linux/videodev2.h>
#include "tools.h" #include "tools.h"
#include "logging.h" #include "logging.h"
typedef struct { typedef struct {
const char *name; const char *name;
uint8_t *data; uint8_t *data;
size_t used; size_t used;
size_t allocated; size_t allocated;
unsigned width;
unsigned height; unsigned width;
unsigned format; unsigned height;
bool online; 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 grab_ts;
long double encode_begin_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); void frame_copy_meta(const frame_s *src, frame_s *dest);
bool frame_compare(const frame_s *a, const frame_s *b); 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); const char *fourcc_to_string(unsigned format, char *buf, size_t size);

View File

@@ -39,12 +39,7 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
struct jpeg_decompress_struct jpeg; struct jpeg_decompress_struct jpeg;
jpeg_create_decompress(&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 // https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
_jpeg_error_manager_s jpeg_error; _jpeg_error_manager_s jpeg_error;
jpeg.err = jpeg_std_error((struct jpeg_error_mgr *)&jpeg_error); jpeg.err = jpeg_std_error((struct jpeg_error_mgr *)&jpeg_error);
jpeg_error.mgr.error_exit = _jpeg_error_handler; 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); 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) { if (decode) {
const unsigned row_stride = jpeg.output_width * jpeg.output_components;
JSAMPARRAY scanlines; 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) { while (jpeg.output_scanline < jpeg.output_height) {
jpeg_read_scanlines(&jpeg, scanlines, 1); 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); jpeg_finish_decompress(&jpeg);
} }
dest->width = jpeg.output_width;
dest->height = jpeg.output_height;
done: done:
jpeg_destroy_decompress(&jpeg); jpeg_destroy_decompress(&jpeg);
return retval; return retval;

View File

@@ -153,6 +153,7 @@ int memsink_server_put(memsink_s *sink, const frame_s *frame) {
COPY(width); COPY(width);
COPY(height); COPY(height);
COPY(format); COPY(format);
COPY(stride);
COPY(online); COPY(online);
COPY(grab_ts); COPY(grab_ts);
COPY(encode_begin_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(width);
COPY(height); COPY(height);
COPY(format); COPY(format);
COPY(stride);
COPY(online); COPY(online);
COPY(grab_ts); COPY(grab_ts);
COPY(encode_begin_ts); COPY(encode_begin_ts);

View File

@@ -51,6 +51,7 @@ typedef struct {
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned format; unsigned format;
unsigned stride;
bool online; bool online;
long double grab_ts; long double grab_ts;
long double encode_begin_ts; long double encode_begin_ts;

View File

@@ -57,7 +57,7 @@ static const struct {
static int _device_open_check_cap(device_s *dev); static int _device_open_check_cap(device_s *dev);
static int _device_open_dv_timings(device_s *dev); static int _device_open_dv_timings(device_s *dev);
static int _device_apply_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_hw_fps(device_s *dev);
static void _device_open_jpeg_quality(device_s *dev); static void _device_open_jpeg_quality(device_s *dev);
static int _device_open_io_method(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) { if (_device_open_dv_timings(dev) < 0) {
goto error; goto error;
} }
if (_device_open_format(dev) < 0) { if (_device_open_format(dev, true) < 0) {
goto error; goto error;
} }
_device_open_hw_fps(dev); _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.width) = RUN(width);
HW(raw.height) = RUN(height); HW(raw.height) = RUN(height);
HW(raw.format) = RUN(format); HW(raw.format) = RUN(format);
HW(raw.stride) = RUN(stride);
HW(raw.online) = true; HW(raw.online) = true;
memcpy(&HW(buf_info), &buf_info, sizeof(struct v4l2_buffer)); memcpy(&HW(buf_info), &buf_info, sizeof(struct v4l2_buffer));
HW(raw.grab_ts) = get_now_monotonic(); HW(raw.grab_ts) = get_now_monotonic();
@@ -464,7 +465,9 @@ static int _device_apply_dv_timings(device_s *dev) {
return 0; 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; struct v4l2_format fmt;
MEMSET_ZERO(fmt); MEMSET_ZERO(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 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.height = RUN(height);
fmt.fmt.pix.pixelformat = dev->format; fmt.fmt.pix.pixelformat = dev->format;
fmt.fmt.pix.field = V4L2_FIELD_ANY; fmt.fmt.pix.field = V4L2_FIELD_ANY;
fmt.fmt.pix.bytesperline = stride;
// Set format // Set format
LOG_DEBUG("Calling ioctl(VIDIOC_S_FMT) ..."); LOG_DEBUG("Calling ioctl(VIDIOC_S_FMT) ...");
if (xioctl(RUN(fd), VIDIOC_S_FMT, &fmt) < 0) { if (xioctl(RUN(fd), VIDIOC_S_FMT, &fmt) < 0) {
LOG_PERROR("Unable to set pixelformat=%s, resolution=%ux%u", LOG_PERROR("Unable to set pixelformat=%s, stride=%u, resolution=%ux%u",
_format_to_string_supported(dev->format), RUN(width), RUN(height)); _format_to_string_supported(dev->format), stride, RUN(width), RUN(height));
return -1; return -1;
} }
// Check resolution // Check resolution
bool retry = false;
if (fmt.fmt.pix.width != RUN(width) || fmt.fmt.pix.height != RUN(height)) { 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)); 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) { if (_device_apply_resolution(dev, fmt.fmt.pix.width, fmt.fmt.pix.height) < 0) {
return -1; return -1;
} }
if (first && retry) {
return _device_open_format(dev, false);
}
LOG_INFO("Using resolution: %ux%u", RUN(width), RUN(height)); LOG_INFO("Using resolution: %ux%u", RUN(width), RUN(height));
// Check format // Check format
@@ -511,6 +520,7 @@ static int _device_open_format(device_s *dev) {
RUN(format) = fmt.fmt.pix.pixelformat; RUN(format) = fmt.fmt.pix.pixelformat;
LOG_INFO("Using pixelformat: %s", _format_to_string_supported(RUN(format))); 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 RUN(raw_size) = fmt.fmt.pix.sizeimage; // Only for userptr
return 0; return 0;
} }

View File

@@ -81,6 +81,7 @@ typedef struct {
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned format; unsigned format;
unsigned stride;
unsigned hw_fps; unsigned hw_fps;
unsigned jpeg_quality; unsigned jpeg_quality;
size_t raw_size; size_t raw_size;

View File

@@ -97,13 +97,13 @@ void encoder_prepare(encoder_s *enc, device_s *dev) {
ER(n_workers) = min_u(enc->n_workers, DR(n_buffers)); 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) { 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; type = ENCODER_TYPE_HW;
} }
if (type == ENCODER_TYPE_HW) { if (type == ENCODER_TYPE_HW) {
if (DR(format) != V4L2_PIX_FMT_MJPEG && DR(format) != V4L2_PIX_FMT_JPEG) { 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; goto use_cpu;
} }
quality = DR(jpeg_quality); quality = DR(jpeg_quality);
@@ -111,8 +111,8 @@ void encoder_prepare(encoder_s *enc, device_s *dev) {
} }
# ifdef WITH_OMX # ifdef WITH_OMX
else if (type == ENCODER_TYPE_OMX) { else if (type == ENCODER_TYPE_OMX) {
if (align_size(DR(width), 32) != DR(width) || align_size(DR(height), 16) != DR(height)) { if (align_size(DR(width), 32) != DR(width)) {
LOG_INFO("Switching to CPU encoder because OMX can't handle %ux%u", DR(width), DR(height)); LOG_INFO("Switching to CPU encoder: OMX can't handle width=%u ...", DR(width));
goto use_cpu; 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) { for (unsigned index = 0; index < ER(n_omxs); ++index) {
if (omx_encoder_prepare(ER(omxs[index]), DR(width), DR(height), DR(format), quality) < 0) { int omx_error = omx_encoder_prepare(ER(omxs[index]), &frame, quality);
LOG_ERROR("Can't prepare OMX encoder, falling back to CPU"); if (omx_error == -2) {
goto use_cpu;
} else if (omx_error < 0) {
goto force_cpu; goto force_cpu;
} }
} }
@@ -154,6 +163,7 @@ void encoder_prepare(encoder_s *enc, device_s *dev) {
# pragma GCC diagnostic push # pragma GCC diagnostic push
// cppcheck-suppress unusedLabel // cppcheck-suppress unusedLabel
force_cpu: force_cpu:
LOG_ERROR("Forced CPU encoder permanently");
cpu_forced = true; cpu_forced = true;
# pragma GCC diagnostic pop # 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); frame_copy_meta(src, dest);
dest->format = V4L2_PIX_FMT_JPEG; dest->format = V4L2_PIX_FMT_JPEG;
dest->stride = 0;
dest->encode_begin_ts = get_now_monotonic(); dest->encode_begin_ts = get_now_monotonic();
dest->used = 0; dest->used = 0;

View File

@@ -113,6 +113,7 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const
uint8_t *line_buffer; uint8_t *line_buffer;
A_CALLOC(line_buffer, frame->width * 3); A_CALLOC(line_buffer, frame->width * 3);
const unsigned padding = frame_get_padding(frame);
const uint8_t *data = frame->data; const uint8_t *data = frame->data;
unsigned z = 0; unsigned z = 0;
@@ -137,9 +138,9 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const
data += 4; data += 4;
} }
} }
data += padding;
JSAMPROW scanlines[1]; JSAMPROW scanlines[1] = {line_buffer};
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1); 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; uint8_t *line_buffer;
A_CALLOC(line_buffer, frame->width * 3); A_CALLOC(line_buffer, frame->width * 3);
const unsigned padding = frame_get_padding(frame);
const uint8_t *data = frame->data; const uint8_t *data = frame->data;
unsigned z = 0; unsigned z = 0;
while (jpeg->next_scanline < frame->height) { while (jpeg->next_scanline < frame->height) {
uint8_t *ptr = line_buffer; 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 y = (!z ? data[1] << 8 : data[3] << 8);
int u = data[0] - 128; int u = data[0] - 128;
int v = data[2] - 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 += 4;
} }
} }
data += padding;
JSAMPROW scanlines[1]; JSAMPROW scanlines[1] = {line_buffer};
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1); 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; uint8_t *line_buffer;
A_CALLOC(line_buffer, frame->width * 3); A_CALLOC(line_buffer, frame->width * 3);
const unsigned padding = frame_get_padding(frame);
const uint8_t *data = frame->data; const uint8_t *data = frame->data;
while (jpeg->next_scanline < frame->height) { while (jpeg->next_scanline < frame->height) {
uint8_t *ptr = line_buffer; 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]; unsigned int two_byte = (data[1] << 8) + data[0];
*(ptr++) = data[1] & 248; // Red *(ptr++) = data[1] & 248; // Red
@@ -206,9 +209,9 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons
data += 2; data += 2;
} }
data += padding;
JSAMPROW scanlines[1]; JSAMPROW scanlines[1] = {line_buffer};
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1); 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) { 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) { while (jpeg->next_scanline < frame->height) {
JSAMPROW scanlines[1]; JSAMPROW scanlines[1] = {data};
scanlines[0] = (uint8_t *)(frame->data + jpeg->next_scanline * frame->width * 3);
jpeg_write_scanlines(jpeg, scanlines, 1); jpeg_write_scanlines(jpeg, scanlines, 1);
data += (jpeg->next_scanline * frame->width * 3) + padding;
} }
} }

View File

@@ -30,7 +30,7 @@ static const OMX_U32 _OUTPUT_PORT = 341;
static int _vcos_semwait(VCOS_SEMAPHORE_T *sem); static int _vcos_semwait(VCOS_SEMAPHORE_T *sem);
static int _omx_init_component(omx_encoder_s *omx); static int _omx_init_component(omx_encoder_s *omx);
static int _omx_init_disable_ports(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_setup_output(omx_encoder_s *omx, unsigned quality);
static int _omx_encoder_clear_ports(omx_encoder_s *omx); static int _omx_encoder_clear_ports(omx_encoder_s *omx);
@@ -106,14 +106,19 @@ void omx_encoder_destroy(omx_encoder_s *omx) {
free(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) { if (omx_component_set_state(&omx->comp, OMX_StateIdle) < 0) {
return -1; return -1;
} }
if (_omx_encoder_clear_ports(omx) < 0) { if (_omx_encoder_clear_ports(omx) < 0) {
return -1; return -1;
} }
if (_omx_setup_input(omx, width, height, format) < 0) { if (_omx_setup_input(omx, frame) < 0) {
return -1; return -1;
} }
if (_omx_setup_output(omx, quality) < 0) { 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; return -1;
} }
dest->width = align_size(src->width, 32);
dest->used = 0; dest->used = 0;
omx->output_available = false; omx->output_available = false;
omx->input_required = true; omx->input_required = true;
@@ -277,7 +284,7 @@ static int _omx_init_disable_ports(omx_encoder_s *omx) {
return 0; 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 ..."); LOG_DEBUG("Setting up OMX JPEG input port ...");
OMX_ERRORTYPE error; 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 # define IFMT(_next) portdef.format.image._next
IFMT(nFrameWidth) = width; IFMT(nFrameWidth) = align_size(frame->width, 32);
IFMT(nFrameHeight) = height; IFMT(nFrameHeight) = frame->height;
IFMT(nStride) = 0; IFMT(nStride) = align_size(frame->width, 32) << 1;
IFMT(nSliceHeight) = align_size(height, 16); IFMT(nSliceHeight) = align_size(frame->height, 16);
IFMT(bFlagErrorConcealment) = OMX_FALSE; IFMT(bFlagErrorConcealment) = OMX_FALSE;
IFMT(eCompressionFormat) = OMX_IMAGE_CodingUnused; IFMT(eCompressionFormat) = OMX_IMAGE_CodingUnused;
portdef.nBufferSize = ((width * height) << 1) * 2; portdef.nBufferSize = ((frame->width * frame->height) << 1) * 2;
switch (format) { switch (frame->format) {
// https://www.fourcc.org/yuv.php // https://www.fourcc.org/yuv.php
// Also see comments inside OMX_IVCommon.h // Also see comments inside OMX_IVCommon.h
case V4L2_PIX_FMT_YUYV: IFMT(eColorFormat) = OMX_COLOR_FormatYCbYCr; break; 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: RGB24 не работает нормально, нижняя половина экрана зеленая.
// FIXME: Китайский EasyCap тоже не работает, мусор на экране. // FIXME: Китайский EasyCap тоже не работает, мусор на экране.
// Вероятно обе проблемы вызваны некорректной реализацией OMX на пае. // Вероятно обе проблемы вызваны некорректной реализацией OMX на пае.
default: assert(0 && "Unsupported input format for OMX JPEG encoder"); default: assert(0 && "Unsupported pixelformat");
} }
# undef IFMT # undef IFMT
@@ -358,43 +365,31 @@ static int _omx_setup_output(omx_encoder_s *omx, unsigned quality) {
return -1; return -1;
} }
{ # define SET_PARAM(_key, _value) { \
OMX_CONFIG_BOOLEANTYPE exif; if ((error = OMX_SetParameter(omx->comp, OMX_IndexParam##_key, _value)) != OMX_ErrorNone) { \
LOG_ERROR_OMX(error, "Can't set OMX param %s", #_key); \
OMX_INIT_STRUCTURE(exif); return -1; \
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;
} }
}
{ OMX_CONFIG_BOOLEANTYPE exif;
OMX_PARAM_IJGSCALINGTYPE ijg; OMX_INIT_STRUCTURE(exif);
exif.bEnabled = OMX_FALSE;
SET_PARAM(BrcmDisableEXIF, &exif);
OMX_INIT_STRUCTURE(ijg); OMX_PARAM_IJGSCALINGTYPE ijg;
ijg.nPortIndex = _OUTPUT_PORT; OMX_INIT_STRUCTURE(ijg);
ijg.bEnabled = OMX_TRUE; ijg.nPortIndex = _OUTPUT_PORT;
ijg.bEnabled = OMX_TRUE;
SET_PARAM(BrcmEnableIJGTableScaling, &ijg);
if ((error = OMX_SetParameter(omx->comp, OMX_IndexParamBrcmEnableIJGTableScaling, &ijg)) != OMX_ErrorNone) { OMX_IMAGE_PARAM_QFACTORTYPE qfactor;
LOG_ERROR_OMX(error, "Can't set OMX JPEG IJG settings"); OMX_INIT_STRUCTURE(qfactor);
return -1; qfactor.nPortIndex = _OUTPUT_PORT;
} qfactor.nQFactor = quality;
} SET_PARAM(QFactor, &qfactor);
{ # undef SET_PARAM
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;
}
}
if (omx_component_enable_port(&omx->comp, _OUTPUT_PORT) < 0) { if (omx_component_enable_port(&omx->comp, _OUTPUT_PORT) < 0) {
return -1; return -1;

View File

@@ -68,5 +68,5 @@ typedef struct {
omx_encoder_s *omx_encoder_init(void); omx_encoder_s *omx_encoder_init(void);
void omx_encoder_destroy(omx_encoder_s *omx); 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); int omx_encoder_compress(omx_encoder_s *omx, const frame_s *src, frame_s *dest);

View File

@@ -95,9 +95,14 @@ void h264_encoder_destroy(h264_encoder_s *enc) {
free(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 ..."); 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; MMAL_STATUS_T error;
# define PREPARE_PORT(_id) { \ # 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) # define IFMT(_next) RUN(input_port->format->_next)
IFMT(type) = MMAL_ES_TYPE_VIDEO; 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_YUYV: IFMT(encoding) = MMAL_ENCODING_YUYV; break;
case V4L2_PIX_FMT_UYVY: IFMT(encoding) = MMAL_ENCODING_UYVY; 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_RGB565: IFMT(encoding) = MMAL_ENCODING_RGB16; break;
case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_MJPEG:
case V4L2_PIX_FMT_JPEG: // See unjpeg.c case V4L2_PIX_FMT_JPEG: // See unjpeg.c
case V4L2_PIX_FMT_RGB24: IFMT(encoding) = MMAL_ENCODING_RGB24; break; 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.width) = align_size(frame->width, 32);
IFMT(es->video.height) = align_size(height, 16); IFMT(es->video.height) = align_size(frame->height, 16);
IFMT(es->video.crop.x) = 0; IFMT(es->video.crop.x) = 0;
IFMT(es->video.crop.y) = 0; IFMT(es->video.crop.y) = 0;
IFMT(es->video.crop.width) = width; IFMT(es->video.crop.width) = frame->width;
IFMT(es->video.crop.height) = height; IFMT(es->video.crop.height) = frame->height;
IFMT(flags) = MMAL_ES_FORMAT_FLAG_FRAMED; IFMT(flags) = MMAL_ES_FORMAT_FLAG_FRAMED;
RUN(input_port->buffer_size) = 1000 * 1000; RUN(input_port->buffer_size) = 1000 * 1000;
RUN(input_port->buffer_num) = RUN(input_port->buffer_num_recommended) * 4; 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(input);
ENABLE_PORT(output); ENABLE_PORT(output);
RUN(width) = width; RUN(width) = frame->width;
RUN(height) = height; RUN(height) = frame->height;
RUN(format) = format; RUN(format) = frame->format;
RUN(stride) = frame->stride;
return 0; return 0;
@@ -247,6 +253,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *enc) {
RUN(width) = 0; RUN(width) = 0;
RUN(height) = 0; RUN(height) = 0;
RUN(format) = 0; RUN(format) = 0;
RUN(stride) = 0;
RUN(last_online) = -1; 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); frame_copy_meta(src, dest);
dest->encode_begin_ts = get_now_monotonic(); dest->encode_begin_ts = get_now_monotonic();
dest->format = V4L2_PIX_FMT_H264; dest->format = V4L2_PIX_FMT_H264;
dest->stride = 0;
if (src->format == V4L2_PIX_FMT_MJPEG || src->format == V4L2_PIX_FMT_JPEG) { if (src->format == V4L2_PIX_FMT_MJPEG || src->format == V4L2_PIX_FMT_JPEG) {
LOG_DEBUG("H264: Input frame format is JPEG; decoding ..."); LOG_DEBUG("H264: Input frame format is JPEG; decoding ...");

View File

@@ -53,6 +53,7 @@ typedef struct {
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned format; unsigned format;
unsigned stride;
} h264_encoder_runtime_s; } h264_encoder_runtime_s;
typedef struct { typedef struct {
@@ -67,5 +68,5 @@ typedef struct {
h264_encoder_s *h264_encoder_init(void); h264_encoder_s *h264_encoder_init(void);
void h264_encoder_destroy(h264_encoder_s *enc); 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); int h264_encoder_compress(h264_encoder_s *enc, const frame_s *src, frame_s *dest);

View File

@@ -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) # define NEQ(_field) (frame->_field != h264->enc->run->_field)
if (NEQ(width) || NEQ(height) || NEQ(format)) { if (NEQ(width) || NEQ(height) || NEQ(format)) {
# undef NEQ # 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->enc->run->format) {
if (h264_encoder_compress(h264->enc, frame, h264->dest) == 0) { if (h264_encoder_compress(h264->enc, frame, h264->dest) == 0) {