mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-01 21:26:33 +00:00
aligned input
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -29,20 +29,28 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ typedef struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned stride;
|
||||
unsigned hw_fps;
|
||||
unsigned jpeg_quality;
|
||||
size_t raw_size;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ...");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user