2020-07-03 13:28:55 +00:00
|
|
|
// Copyright 2019-2020, Collabora, Ltd.
|
2019-07-23 12:38:58 +00:00
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
2019-08-20 11:17:24 +00:00
|
|
|
* @brief @ref xrt_frame_sink converters and other helpers.
|
2019-07-23 12:38:58 +00:00
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
|
|
* @ingroup aux_util
|
|
|
|
*/
|
|
|
|
|
2020-03-02 15:44:00 +00:00
|
|
|
#include "xrt/xrt_config_have.h"
|
2020-07-03 13:28:55 +00:00
|
|
|
#include "util/u_logging.h"
|
2019-07-23 12:38:58 +00:00
|
|
|
#include "util/u_misc.h"
|
|
|
|
#include "util/u_sink.h"
|
2019-08-22 13:15:41 +00:00
|
|
|
#include "util/u_frame.h"
|
2019-07-23 12:38:58 +00:00
|
|
|
#include "util/u_format.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_JPEG
|
|
|
|
#include "jpeglib.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Structs
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2020-06-03 16:43:30 +00:00
|
|
|
/*!
|
|
|
|
* An @ref xrt_frame_sink that converts frames.
|
|
|
|
* @implements xrt_frame_sink
|
|
|
|
* @implements xrt_frame_node
|
|
|
|
*/
|
2019-07-23 12:38:58 +00:00
|
|
|
struct u_sink_converter
|
|
|
|
{
|
2019-08-20 11:17:24 +00:00
|
|
|
struct xrt_frame_sink base;
|
2019-08-22 13:15:41 +00:00
|
|
|
struct xrt_frame_node node;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
2019-08-20 11:17:24 +00:00
|
|
|
struct xrt_frame_sink *downstream;
|
2020-11-25 20:37:57 +00:00
|
|
|
struct xrt_frame_sink *downstream2;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
enum xrt_format format;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* YUV functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
clamp_to_byte(int v)
|
|
|
|
{
|
|
|
|
if (v < 0) {
|
|
|
|
return 0;
|
2019-08-16 21:56:47 +00:00
|
|
|
}
|
|
|
|
if (v >= 255) {
|
2019-07-23 12:38:58 +00:00
|
|
|
return 255;
|
|
|
|
}
|
2019-08-16 21:56:47 +00:00
|
|
|
return v;
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t
|
|
|
|
YUV444_to_RGBX8888(int y, int u, int v)
|
|
|
|
{
|
|
|
|
int C = y - 16;
|
|
|
|
int D = u - 128;
|
|
|
|
int E = v - 128;
|
|
|
|
|
|
|
|
int R = clamp_to_byte((298 * C + 409 * E + 128) >> 8);
|
|
|
|
int G = clamp_to_byte((298 * C - 100 * D - 209 * E + 128) >> 8);
|
|
|
|
int B = clamp_to_byte((298 * C + 516 * D + 128) >> 8);
|
|
|
|
|
|
|
|
return B << 16 | G << 8 | R;
|
|
|
|
}
|
|
|
|
|
2019-08-17 09:34:40 +00:00
|
|
|
#undef USE_TABLE
|
|
|
|
#ifdef USE_TABLE
|
|
|
|
static uint32_t lookup_YUV_to_RGBX[256][256][256] = {0};
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
generate_lookup_YUV_to_RGBX()
|
|
|
|
{
|
|
|
|
if (lookup_YUV_to_RGBX[255][255][255] != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int y;
|
|
|
|
int u;
|
|
|
|
int v;
|
|
|
|
|
|
|
|
for (y = 0; y < 256; y++) {
|
|
|
|
for (u = 0; u < 256; u++) {
|
|
|
|
for (v = 0; v < 256; v++) {
|
|
|
|
lookup_YUV_to_RGBX[y][u][v] =
|
|
|
|
YUV444_to_RGBX8888(y, u, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-17 09:34:40 +00:00
|
|
|
#endif
|
2019-07-23 12:38:58 +00:00
|
|
|
|
2020-11-30 15:39:30 +00:00
|
|
|
XRT_MAYBE_UNUSED inline static void
|
2020-04-24 16:48:42 +00:00
|
|
|
YUYV422_to_R8G8B8X8(const uint8_t *input, uint32_t *rgb1, uint32_t *rgb2)
|
2019-07-23 12:38:58 +00:00
|
|
|
{
|
|
|
|
uint8_t y0 = input[0];
|
|
|
|
uint8_t u = input[1];
|
|
|
|
uint8_t y1 = input[2];
|
|
|
|
uint8_t v = input[3];
|
|
|
|
|
2019-08-17 09:34:40 +00:00
|
|
|
#ifdef USE_TABLE
|
2019-07-23 12:38:58 +00:00
|
|
|
*rgb1 = lookup_YUV_to_RGBX[y0][u][v];
|
|
|
|
*rgb2 = lookup_YUV_to_RGBX[y1][u][v];
|
2019-08-17 09:34:40 +00:00
|
|
|
#else
|
|
|
|
*rgb1 = YUV444_to_RGBX8888(y0, u, v);
|
|
|
|
*rgb2 = YUV444_to_RGBX8888(y1, u, v);
|
|
|
|
#endif
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline static void
|
2020-04-24 16:48:42 +00:00
|
|
|
YUYV422_to_R8G8B8(const uint8_t *input, uint8_t *dst)
|
2019-07-23 12:38:58 +00:00
|
|
|
{
|
|
|
|
uint8_t y0 = input[0];
|
|
|
|
uint8_t u = input[1];
|
|
|
|
uint8_t y1 = input[2];
|
|
|
|
uint8_t v = input[3];
|
|
|
|
|
2019-08-17 09:34:40 +00:00
|
|
|
#ifdef USE_TABLE
|
2019-07-23 12:38:58 +00:00
|
|
|
uint8_t *rgb1 = (uint8_t *)&lookup_YUV_to_RGBX[y0][u][v];
|
|
|
|
uint8_t *rgb2 = (uint8_t *)&lookup_YUV_to_RGBX[y1][u][v];
|
2019-08-17 09:34:40 +00:00
|
|
|
#else
|
|
|
|
uint32_t rgb1v = YUV444_to_RGBX8888(y0, u, v);
|
|
|
|
uint32_t rgb2v = YUV444_to_RGBX8888(y1, u, v);
|
|
|
|
uint8_t *rgb1 = (uint8_t *)&rgb1v;
|
|
|
|
uint8_t *rgb2 = (uint8_t *)&rgb2v;
|
|
|
|
#endif
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
dst[0] = rgb1[0];
|
|
|
|
dst[1] = rgb1[1];
|
|
|
|
dst[2] = rgb1[2];
|
|
|
|
dst[3] = rgb2[0];
|
|
|
|
dst[4] = rgb2[1];
|
|
|
|
dst[5] = rgb2[2];
|
|
|
|
}
|
|
|
|
|
2020-04-24 19:00:23 +00:00
|
|
|
inline static void
|
|
|
|
UYVY422_to_R8G8B8(const uint8_t *input, uint8_t *dst)
|
|
|
|
{
|
|
|
|
uint8_t u = input[0];
|
|
|
|
uint8_t y0 = input[1];
|
|
|
|
uint8_t v = input[2];
|
|
|
|
uint8_t y1 = input[3];
|
|
|
|
|
|
|
|
#ifdef USE_TABLE
|
|
|
|
uint8_t *rgb1 = (uint8_t *)&lookup_YUV_to_RGBX[y0][u][v];
|
|
|
|
uint8_t *rgb2 = (uint8_t *)&lookup_YUV_to_RGBX[y1][u][v];
|
|
|
|
#else
|
|
|
|
uint32_t rgb1v = YUV444_to_RGBX8888(y0, u, v);
|
|
|
|
uint32_t rgb2v = YUV444_to_RGBX8888(y1, u, v);
|
|
|
|
uint8_t *rgb1 = (uint8_t *)&rgb1v;
|
|
|
|
uint8_t *rgb2 = (uint8_t *)&rgb2v;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dst[0] = rgb1[0];
|
|
|
|
dst[1] = rgb1[1];
|
|
|
|
dst[2] = rgb1[2];
|
|
|
|
dst[3] = rgb2[0];
|
|
|
|
dst[4] = rgb2[1];
|
|
|
|
dst[5] = rgb2[2];
|
|
|
|
}
|
|
|
|
|
2020-01-18 22:16:34 +00:00
|
|
|
inline static void
|
|
|
|
YUV444_to_R8G8B8(const uint8_t *input, uint8_t *dst)
|
|
|
|
{
|
|
|
|
uint8_t y = input[0];
|
|
|
|
uint8_t u = input[1];
|
|
|
|
uint8_t v = input[2];
|
|
|
|
|
|
|
|
#ifdef USE_TABLE
|
|
|
|
uint8_t *rgb = (uint8_t *)&lookup_YUV_to_RGBX[y][u][v];
|
|
|
|
#else
|
|
|
|
uint32_t rgbv = YUV444_to_RGBX8888(y, u, v);
|
|
|
|
uint8_t *rgb = (uint8_t *)&rgbv;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dst[0] = rgb[0];
|
|
|
|
dst[1] = rgb[1];
|
|
|
|
dst[2] = rgb[2];
|
|
|
|
}
|
|
|
|
|
2019-07-23 12:38:58 +00:00
|
|
|
static void
|
2020-11-25 14:09:34 +00:00
|
|
|
from_YUYV422_to_R8G8B8(struct xrt_frame *dst_frame,
|
2020-04-24 16:48:42 +00:00
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
size_t stride,
|
|
|
|
const uint8_t *data)
|
2019-07-23 12:38:58 +00:00
|
|
|
{
|
2019-08-22 13:15:41 +00:00
|
|
|
for (uint32_t y = 0; y < h; y++) {
|
|
|
|
for (uint32_t x = 0; x < w; x += 2) {
|
2019-07-23 12:38:58 +00:00
|
|
|
const uint8_t *src = data;
|
2020-11-25 14:09:34 +00:00
|
|
|
uint8_t *dst = dst_frame->data;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
src = src + (y * stride) + (x * 2);
|
2020-11-25 14:09:34 +00:00
|
|
|
dst = dst + (y * dst_frame->stride) + (x * 3);
|
2020-04-24 16:48:42 +00:00
|
|
|
YUYV422_to_R8G8B8(src, dst);
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-24 19:00:23 +00:00
|
|
|
static void
|
2020-11-25 14:09:34 +00:00
|
|
|
from_UYVY422_to_R8G8B8(struct xrt_frame *dst_frame,
|
2020-04-24 19:00:23 +00:00
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
size_t stride,
|
|
|
|
const uint8_t *data)
|
|
|
|
{
|
|
|
|
for (uint32_t y = 0; y < h; y++) {
|
|
|
|
for (uint32_t x = 0; x < w; x += 2) {
|
|
|
|
const uint8_t *src = data;
|
2020-11-25 14:09:34 +00:00
|
|
|
uint8_t *dst = dst_frame->data;
|
2020-04-24 19:00:23 +00:00
|
|
|
|
|
|
|
src = src + (y * stride) + (x * 2);
|
2020-11-25 14:09:34 +00:00
|
|
|
dst = dst + (y * dst_frame->stride) + (x * 3);
|
2020-04-24 19:00:23 +00:00
|
|
|
UYVY422_to_R8G8B8(src, dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-18 22:16:34 +00:00
|
|
|
static void
|
2020-11-25 14:09:34 +00:00
|
|
|
from_YUV888_to_R8G8B8(struct xrt_frame *dst_frame,
|
2020-01-18 22:16:34 +00:00
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
size_t stride,
|
|
|
|
const uint8_t *data)
|
|
|
|
{
|
|
|
|
for (uint32_t y = 0; y < h; y++) {
|
|
|
|
for (uint32_t x = 0; x < w; x++) {
|
|
|
|
const uint8_t *src = data;
|
2020-11-25 14:09:34 +00:00
|
|
|
uint8_t *dst = dst_frame->data;
|
2020-01-18 22:16:34 +00:00
|
|
|
|
|
|
|
src = src + (y * stride) + (x * 3);
|
2020-11-25 14:09:34 +00:00
|
|
|
dst = dst + (y * dst_frame->stride) + (x * 3);
|
2020-01-18 22:16:34 +00:00
|
|
|
YUV444_to_R8G8B8(src, dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* MJPEG
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_JPEG
|
2019-11-23 00:01:05 +00:00
|
|
|
static bool
|
|
|
|
check_header(size_t size, const uint8_t *data)
|
|
|
|
{
|
|
|
|
if (size < 16) {
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Invalid JPEG file size! %u", (uint32_t)size);
|
2019-11-23 00:01:05 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[0] != 0xFF || data[1] != 0xD8) {
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Invalid file header! 0x%02X 0x%02X", data[0], data[1]);
|
2019-11-23 00:01:05 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2020-11-25 14:09:34 +00:00
|
|
|
from_MJPEG_to_R8G8B8(struct xrt_frame *dst_frame,
|
2019-07-23 12:38:58 +00:00
|
|
|
size_t size,
|
|
|
|
const uint8_t *data)
|
|
|
|
{
|
2019-11-23 00:01:05 +00:00
|
|
|
if (!check_header(size, data)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
struct jpeg_decompress_struct cinfo = {0};
|
|
|
|
struct jpeg_error_mgr jerr = {0};
|
|
|
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
jerr.trace_level = 0;
|
|
|
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
jpeg_mem_src(&cinfo, data, size);
|
2019-11-23 00:01:05 +00:00
|
|
|
|
|
|
|
int ret = jpeg_read_header(&cinfo, TRUE);
|
|
|
|
if (ret != JPEG_HEADER_OK) {
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
cinfo.out_color_space = JCS_RGB;
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
2020-11-25 14:09:34 +00:00
|
|
|
uint8_t *moving_ptr = dst_frame->data;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
uint32_t scanlines_read = 0;
|
|
|
|
while (scanlines_read < cinfo.image_height) {
|
|
|
|
int read_count = jpeg_read_scanlines(&cinfo, &moving_ptr, 16);
|
2020-11-25 14:09:34 +00:00
|
|
|
moving_ptr += read_count * dst_frame->stride;
|
2019-07-23 12:38:58 +00:00
|
|
|
scanlines_read += read_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
2019-11-23 00:01:05 +00:00
|
|
|
|
|
|
|
return true;
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
2019-08-05 18:20:53 +00:00
|
|
|
|
2019-11-23 00:01:05 +00:00
|
|
|
static bool
|
2020-11-25 14:09:34 +00:00
|
|
|
from_MJPEG_to_YUV888(struct xrt_frame *dst_frame,
|
2019-08-05 18:20:53 +00:00
|
|
|
size_t size,
|
|
|
|
const uint8_t *data)
|
|
|
|
{
|
2019-11-23 00:01:05 +00:00
|
|
|
if (!check_header(size, data)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-05 18:20:53 +00:00
|
|
|
|
|
|
|
struct jpeg_decompress_struct cinfo = {0};
|
|
|
|
struct jpeg_error_mgr jerr = {0};
|
|
|
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
jerr.trace_level = 0;
|
|
|
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
jpeg_mem_src(&cinfo, data, size);
|
2019-11-23 00:01:05 +00:00
|
|
|
|
|
|
|
int ret = jpeg_read_header(&cinfo, TRUE);
|
|
|
|
if (ret != JPEG_HEADER_OK) {
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-05 18:20:53 +00:00
|
|
|
|
|
|
|
cinfo.out_color_space = JCS_YCbCr;
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
2020-11-25 14:09:34 +00:00
|
|
|
uint8_t *moving_ptr = dst_frame->data;
|
2019-08-05 18:20:53 +00:00
|
|
|
|
|
|
|
uint32_t scanlines_read = 0;
|
|
|
|
while (scanlines_read < cinfo.image_height) {
|
|
|
|
int read_count = jpeg_read_scanlines(&cinfo, &moving_ptr, 16);
|
2020-11-25 14:09:34 +00:00
|
|
|
moving_ptr += read_count * dst_frame->stride;
|
2019-08-05 18:20:53 +00:00
|
|
|
scanlines_read += read_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
2019-11-23 00:01:05 +00:00
|
|
|
|
|
|
|
return true;
|
2019-08-05 18:20:53 +00:00
|
|
|
}
|
2019-07-23 12:38:58 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Misc functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2020-11-30 17:36:41 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* Creates a frame that the conversion should happen to.
|
|
|
|
*
|
|
|
|
* @todo Allocate from a pool of frames.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
create_frame_with_format(struct xrt_frame *xf,
|
|
|
|
enum xrt_format format,
|
|
|
|
struct xrt_frame **out_frame)
|
2019-07-23 12:38:58 +00:00
|
|
|
{
|
2020-11-30 17:36:41 +00:00
|
|
|
struct xrt_frame *frame = NULL;
|
|
|
|
u_frame_create_one_off(format, xf->width, xf->height, &frame);
|
|
|
|
if (frame == NULL) {
|
|
|
|
U_LOG_E("Failed to create target frame!");
|
|
|
|
*out_frame = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
frame->timestamp = xf->timestamp;
|
|
|
|
frame->source_timestamp = xf->source_timestamp;
|
|
|
|
frame->source_sequence = xf->source_sequence;
|
|
|
|
frame->source_id = xf->source_id;
|
|
|
|
frame->stereo_format = xf->stereo_format;
|
|
|
|
|
|
|
|
*out_frame = frame;
|
|
|
|
|
|
|
|
return true;
|
2019-08-05 18:20:53 +00:00
|
|
|
}
|
|
|
|
|
2019-10-19 22:14:19 +00:00
|
|
|
static void
|
|
|
|
receive_frame_r8g8b8_or_l8(struct xrt_frame_sink *xs, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s = (struct u_sink_converter *)xs;
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
struct xrt_frame *converted = NULL;
|
|
|
|
|
2019-10-19 22:14:19 +00:00
|
|
|
switch (xf->format) {
|
|
|
|
case XRT_FORMAT_L8:
|
|
|
|
case XRT_FORMAT_R8G8B8:
|
|
|
|
s->downstream->push_frame(s->downstream, xf);
|
|
|
|
return;
|
2020-04-24 16:48:42 +00:00
|
|
|
case XRT_FORMAT_YUYV422:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_YUYV422_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2019-10-19 22:14:19 +00:00
|
|
|
break;
|
2020-04-24 19:00:23 +00:00
|
|
|
case XRT_FORMAT_UYVY422:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_UYVY422_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2020-04-24 19:00:23 +00:00
|
|
|
break;
|
2020-01-18 22:16:34 +00:00
|
|
|
case XRT_FORMAT_YUV888:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_YUV888_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2020-01-18 22:16:34 +00:00
|
|
|
break;
|
2019-10-19 22:14:19 +00:00
|
|
|
#ifdef XRT_HAVE_JPEG
|
|
|
|
case XRT_FORMAT_MJPEG:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
if (!from_MJPEG_to_R8G8B8(converted, xf->size, xf->data)) {
|
2019-11-23 00:01:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-10-19 22:14:19 +00:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Can not convert from '%s' to R8G8B8 or L8!",
|
2019-10-19 22:14:19 +00:00
|
|
|
u_format_str(xf->format));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
s->downstream->push_frame(s->downstream, converted);
|
|
|
|
|
|
|
|
// Refcount in case it's being held downstream.
|
|
|
|
xrt_frame_reference(&converted, NULL);
|
2019-10-19 22:14:19 +00:00
|
|
|
}
|
|
|
|
|
2019-08-05 18:20:53 +00:00
|
|
|
static void
|
|
|
|
receive_frame_r8g8b8(struct xrt_frame_sink *xs, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s = (struct u_sink_converter *)xs;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
struct xrt_frame *converted = NULL;
|
|
|
|
|
2019-07-23 12:38:58 +00:00
|
|
|
switch (xf->format) {
|
2019-08-05 18:20:53 +00:00
|
|
|
case XRT_FORMAT_R8G8B8:
|
|
|
|
s->downstream->push_frame(s->downstream, xf);
|
|
|
|
return;
|
2020-04-24 16:48:42 +00:00
|
|
|
case XRT_FORMAT_YUYV422:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_YUYV422_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2019-07-23 12:38:58 +00:00
|
|
|
break;
|
2020-04-24 19:00:23 +00:00
|
|
|
case XRT_FORMAT_UYVY422:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_UYVY422_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2020-04-24 19:00:23 +00:00
|
|
|
break;
|
2020-01-18 22:16:34 +00:00
|
|
|
case XRT_FORMAT_YUV888:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
from_YUV888_to_R8G8B8(converted, xf->width, xf->height,
|
2020-11-25 14:09:34 +00:00
|
|
|
xf->stride, xf->data);
|
2020-01-18 22:16:34 +00:00
|
|
|
break;
|
2019-07-23 12:38:58 +00:00
|
|
|
#ifdef XRT_HAVE_JPEG
|
|
|
|
case XRT_FORMAT_MJPEG:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_R8G8B8,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
if (!from_MJPEG_to_R8G8B8(converted, xf->size, xf->data)) {
|
2019-11-23 00:01:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-07-23 12:38:58 +00:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Can not convert from '%s' to R8G8B8!",
|
2019-07-23 12:38:58 +00:00
|
|
|
u_format_str(xf->format));
|
2019-08-05 18:20:53 +00:00
|
|
|
return;
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
s->downstream->push_frame(s->downstream, converted);
|
|
|
|
|
|
|
|
// Refcount in case it's being held downstream.
|
|
|
|
xrt_frame_reference(&converted, NULL);
|
2019-08-05 18:20:53 +00:00
|
|
|
}
|
2019-07-23 12:38:58 +00:00
|
|
|
|
2020-01-17 22:24:54 +00:00
|
|
|
static void
|
2020-04-24 19:00:23 +00:00
|
|
|
receive_frame_yuv_yuyv_uyvy_or_l8(struct xrt_frame_sink *xs,
|
|
|
|
struct xrt_frame *xf)
|
2020-01-17 22:24:54 +00:00
|
|
|
{
|
|
|
|
struct u_sink_converter *s = (struct u_sink_converter *)xs;
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
struct xrt_frame *converted = NULL;
|
2020-01-17 22:24:54 +00:00
|
|
|
|
|
|
|
switch (xf->format) {
|
|
|
|
case XRT_FORMAT_L8:
|
2020-04-24 16:48:42 +00:00
|
|
|
case XRT_FORMAT_YUYV422:
|
2020-04-24 19:00:23 +00:00
|
|
|
case XRT_FORMAT_UYVY422:
|
2020-01-17 22:24:54 +00:00
|
|
|
case XRT_FORMAT_YUV888:
|
|
|
|
s->downstream->push_frame(s->downstream, xf);
|
|
|
|
return;
|
|
|
|
#ifdef XRT_HAVE_JPEG
|
|
|
|
case XRT_FORMAT_MJPEG:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_YUV888,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
if (!from_MJPEG_to_YUV888(converted, xf->size, xf->data)) {
|
2020-01-17 22:24:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E(
|
|
|
|
"Can not convert from '%s' to either YUV, YUYV, UYVY or "
|
|
|
|
"L8!",
|
|
|
|
u_format_str(xf->format));
|
2020-01-17 22:24:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
s->downstream->push_frame(s->downstream, converted);
|
|
|
|
|
|
|
|
// Refcount in case it's being held downstream.
|
|
|
|
xrt_frame_reference(&converted, NULL);
|
2020-01-17 22:24:54 +00:00
|
|
|
}
|
|
|
|
|
2019-08-05 18:20:53 +00:00
|
|
|
static void
|
|
|
|
receive_frame_yuv_or_yuyv(struct xrt_frame_sink *xs, struct xrt_frame *xf)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s = (struct u_sink_converter *)xs;
|
2019-07-23 12:38:58 +00:00
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
struct xrt_frame *converted = NULL;
|
2019-08-05 18:20:53 +00:00
|
|
|
|
|
|
|
switch (xf->format) {
|
2020-04-24 16:48:42 +00:00
|
|
|
case XRT_FORMAT_YUYV422:
|
2019-08-05 18:20:53 +00:00
|
|
|
case XRT_FORMAT_YUV888:
|
|
|
|
s->downstream->push_frame(s->downstream, xf);
|
|
|
|
return;
|
|
|
|
#ifdef XRT_HAVE_JPEG
|
|
|
|
case XRT_FORMAT_MJPEG:
|
2020-11-30 17:36:41 +00:00
|
|
|
if (!create_frame_with_format(xf, XRT_FORMAT_YUV888,
|
|
|
|
&converted)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-25 15:05:43 +00:00
|
|
|
if (!from_MJPEG_to_YUV888(converted, xf->size, xf->data)) {
|
2019-11-23 00:01:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-08-05 18:20:53 +00:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Can not convert from '%s' to either YUV or YUYV!",
|
|
|
|
u_format_str(xf->format));
|
2019-08-05 18:20:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-25 15:05:43 +00:00
|
|
|
s->downstream->push_frame(s->downstream, converted);
|
|
|
|
|
|
|
|
// Refcount in case it's being held downstream.
|
|
|
|
xrt_frame_reference(&converted, NULL);
|
2019-07-23 12:38:58 +00:00
|
|
|
}
|
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
static void
|
|
|
|
break_apart(struct xrt_frame_node *node)
|
|
|
|
{}
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy(struct xrt_frame_node *node)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s =
|
|
|
|
container_of(node, struct u_sink_converter, node);
|
|
|
|
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* "Exported" functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2019-08-22 13:15:41 +00:00
|
|
|
u_sink_create_format_converter(struct xrt_frame_context *xfctx,
|
|
|
|
enum xrt_format f,
|
2019-08-20 11:17:24 +00:00
|
|
|
struct xrt_frame_sink *downstream,
|
|
|
|
struct xrt_frame_sink **out_xfs)
|
2019-07-23 12:38:58 +00:00
|
|
|
{
|
|
|
|
if (f != XRT_FORMAT_R8G8B8) {
|
2020-07-03 13:28:55 +00:00
|
|
|
U_LOG_E("Format '%s' not supported", u_format_str(f));
|
2019-07-23 12:38:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-17 09:34:40 +00:00
|
|
|
#ifdef USE_TABLE
|
2019-07-23 12:38:58 +00:00
|
|
|
generate_lookup_YUV_to_RGBX();
|
2019-08-17 09:34:40 +00:00
|
|
|
#endif
|
2019-07-23 12:38:58 +00:00
|
|
|
|
|
|
|
struct u_sink_converter *s = U_TYPED_CALLOC(struct u_sink_converter);
|
2019-08-05 18:20:53 +00:00
|
|
|
s->base.push_frame = receive_frame_r8g8b8;
|
2019-08-22 13:15:41 +00:00
|
|
|
s->node.break_apart = break_apart;
|
|
|
|
s->node.destroy = destroy;
|
2019-07-23 12:38:58 +00:00
|
|
|
s->downstream = downstream;
|
2019-08-05 18:20:53 +00:00
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
xrt_frame_context_add(xfctx, &s->node);
|
|
|
|
|
2019-08-05 18:20:53 +00:00
|
|
|
*out_xfs = &s->base;
|
|
|
|
}
|
|
|
|
|
2019-10-19 22:14:19 +00:00
|
|
|
void
|
|
|
|
u_sink_create_to_r8g8b8_or_l8(struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_frame_sink *downstream,
|
|
|
|
struct xrt_frame_sink **out_xfs)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s = U_TYPED_CALLOC(struct u_sink_converter);
|
|
|
|
s->base.push_frame = receive_frame_r8g8b8_or_l8;
|
|
|
|
s->node.break_apart = break_apart;
|
|
|
|
s->node.destroy = destroy;
|
|
|
|
s->downstream = downstream;
|
|
|
|
|
|
|
|
#ifdef USE_TABLE
|
|
|
|
generate_lookup_YUV_to_RGBX();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
xrt_frame_context_add(xfctx, &s->node);
|
|
|
|
|
|
|
|
*out_xfs = &s->base;
|
|
|
|
}
|
|
|
|
|
2020-01-17 22:24:54 +00:00
|
|
|
void
|
2020-04-24 19:00:23 +00:00
|
|
|
u_sink_create_to_yuv_yuyv_uyvy_or_l8(struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_frame_sink *downstream,
|
|
|
|
struct xrt_frame_sink **out_xfs)
|
2020-01-17 22:24:54 +00:00
|
|
|
{
|
|
|
|
struct u_sink_converter *s = U_TYPED_CALLOC(struct u_sink_converter);
|
2020-04-24 19:00:23 +00:00
|
|
|
s->base.push_frame = receive_frame_yuv_yuyv_uyvy_or_l8;
|
2020-01-17 22:24:54 +00:00
|
|
|
s->node.break_apart = break_apart;
|
|
|
|
s->node.destroy = destroy;
|
|
|
|
s->downstream = downstream;
|
|
|
|
|
|
|
|
xrt_frame_context_add(xfctx, &s->node);
|
|
|
|
|
|
|
|
*out_xfs = &s->base;
|
|
|
|
}
|
|
|
|
|
2019-08-05 18:20:53 +00:00
|
|
|
void
|
2019-08-22 13:15:41 +00:00
|
|
|
u_sink_create_to_yuv_or_yuyv(struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_frame_sink *downstream,
|
2019-08-05 18:20:53 +00:00
|
|
|
struct xrt_frame_sink **out_xfs)
|
|
|
|
{
|
|
|
|
struct u_sink_converter *s = U_TYPED_CALLOC(struct u_sink_converter);
|
|
|
|
s->base.push_frame = receive_frame_yuv_or_yuyv;
|
2019-08-22 13:15:41 +00:00
|
|
|
s->node.break_apart = break_apart;
|
|
|
|
s->node.destroy = destroy;
|
2019-08-05 18:20:53 +00:00
|
|
|
s->downstream = downstream;
|
|
|
|
|
2019-08-22 13:15:41 +00:00
|
|
|
xrt_frame_context_add(xfctx, &s->node);
|
|
|
|
|
2019-07-23 12:38:58 +00:00
|
|
|
*out_xfs = &s->base;
|
|
|
|
}
|