mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-15 03:53:43 +00:00
Add Support for YUV420 and YVU variants. (#276)
* Add Support fo YUV420 and 410 and YVU variants. * Add new formats to the help messaging * Remove YUV410 supprt since M2M encoder on Pi cannot convert it * Cleanups requested by @mdevaev * Change to use u8 per @mdevaev
This commit is contained in:
@@ -72,6 +72,8 @@ static const struct {
|
|||||||
{"BGR24", V4L2_PIX_FMT_BGR24},
|
{"BGR24", V4L2_PIX_FMT_BGR24},
|
||||||
{"MJPEG", V4L2_PIX_FMT_MJPEG},
|
{"MJPEG", V4L2_PIX_FMT_MJPEG},
|
||||||
{"JPEG", V4L2_PIX_FMT_JPEG},
|
{"JPEG", V4L2_PIX_FMT_JPEG},
|
||||||
|
{"YU12", V4L2_PIX_FMT_YUV420},
|
||||||
|
{"YV12", V4L2_PIX_FMT_YVU420},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
#define US_VIDEO_MAX_FPS ((uint)120)
|
#define US_VIDEO_MAX_FPS ((uint)120)
|
||||||
|
|
||||||
#define US_STANDARDS_STR "PAL, NTSC, SECAM"
|
#define US_STANDARDS_STR "PAL, NTSC, SECAM"
|
||||||
#define US_FORMATS_STR "YUYV, YVYU, UYVY, RGB565, RGB24, BGR24, MJPEG, JPEG"
|
#define US_FORMATS_STR "YUYV, YVYU, UYVY, YU12, YV12, RGB565, RGB24, BGR24, MJPEG, JPEG"
|
||||||
#define US_IO_METHODS_STR "MMAP, USERPTR"
|
#define US_IO_METHODS_STR "MMAP, USERPTR"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ bool us_frame_compare(const us_frame_s *a, const us_frame_s *b) {
|
|||||||
uint us_frame_get_padding(const us_frame_s *frame) {
|
uint us_frame_get_padding(const us_frame_s *frame) {
|
||||||
uint bytes_per_pixel = 0;
|
uint bytes_per_pixel = 0;
|
||||||
switch (frame->format) {
|
switch (frame->format) {
|
||||||
|
case V4L2_PIX_FMT_YVU420:
|
||||||
|
case V4L2_PIX_FMT_YUV420: bytes_per_pixel = 1; break;
|
||||||
case V4L2_PIX_FMT_YUYV:
|
case V4L2_PIX_FMT_YUYV:
|
||||||
case V4L2_PIX_FMT_YVYU:
|
case V4L2_PIX_FMT_YVYU:
|
||||||
case V4L2_PIX_FMT_UYVY:
|
case V4L2_PIX_FMT_UYVY:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "encoder.h"
|
#include "encoder.h"
|
||||||
|
#include "../../../libs/logging.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct jpeg_destination_mgr mgr; // Default manager
|
struct jpeg_destination_mgr mgr; // Default manager
|
||||||
@@ -38,6 +38,7 @@ typedef struct {
|
|||||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame);
|
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame);
|
||||||
|
|
||||||
static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
|
static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
|
||||||
#ifndef JCS_EXTENSIONS
|
#ifndef JCS_EXTENSIONS
|
||||||
@@ -69,9 +70,11 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned q
|
|||||||
switch (src->format) {
|
switch (src->format) {
|
||||||
case V4L2_PIX_FMT_YUYV:
|
case V4L2_PIX_FMT_YUYV:
|
||||||
case V4L2_PIX_FMT_YVYU:
|
case V4L2_PIX_FMT_YVYU:
|
||||||
case V4L2_PIX_FMT_UYVY: jpeg.in_color_space = JCS_YCbCr; break;
|
case V4L2_PIX_FMT_UYVY:
|
||||||
|
case V4L2_PIX_FMT_YUV420:
|
||||||
|
case V4L2_PIX_FMT_YVU420: jpeg.in_color_space = JCS_YCbCr; break;
|
||||||
# ifdef JCS_EXTENSIONS
|
# ifdef JCS_EXTENSIONS
|
||||||
case V4L2_PIX_FMT_BGR24: jpeg.in_color_space = JCS_EXT_BGR; break;
|
case V4L2_PIX_FMT_BGR24: jpeg.in_color_space = JCS_EXT_BGR; break;
|
||||||
# endif
|
# endif
|
||||||
default: jpeg.in_color_space = JCS_RGB; break;
|
default: jpeg.in_color_space = JCS_RGB; break;
|
||||||
}
|
}
|
||||||
@@ -86,6 +89,8 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned q
|
|||||||
case V4L2_PIX_FMT_YUYV:
|
case V4L2_PIX_FMT_YUYV:
|
||||||
case V4L2_PIX_FMT_YVYU:
|
case V4L2_PIX_FMT_YVYU:
|
||||||
case V4L2_PIX_FMT_UYVY: _jpeg_write_scanlines_yuv(&jpeg, src); break;
|
case V4L2_PIX_FMT_UYVY: _jpeg_write_scanlines_yuv(&jpeg, src); break;
|
||||||
|
case V4L2_PIX_FMT_YUV420:
|
||||||
|
case V4L2_PIX_FMT_YVU420: _jpeg_write_scanlines_yuv_planar(&jpeg, src); break;
|
||||||
case V4L2_PIX_FMT_RGB565: _jpeg_write_scanlines_rgb565(&jpeg, src); break;
|
case V4L2_PIX_FMT_RGB565: _jpeg_write_scanlines_rgb565(&jpeg, src); break;
|
||||||
case V4L2_PIX_FMT_RGB24: _jpeg_write_scanlines_rgb24(&jpeg, src); break;
|
case V4L2_PIX_FMT_RGB24: _jpeg_write_scanlines_rgb24(&jpeg, src); break;
|
||||||
case V4L2_PIX_FMT_BGR24:
|
case V4L2_PIX_FMT_BGR24:
|
||||||
@@ -121,19 +126,19 @@ static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
uint8_t *line_buf;
|
u8 *line_buf;
|
||||||
US_CALLOC(line_buf, frame->width * 3);
|
US_CALLOC(line_buf, frame->width * 3);
|
||||||
|
|
||||||
const unsigned padding = us_frame_get_padding(frame);
|
const unsigned padding = us_frame_get_padding(frame);
|
||||||
const uint8_t *data = frame->data;
|
const u8 *data = frame->data;
|
||||||
|
|
||||||
while (jpeg->next_scanline < frame->height) {
|
while (jpeg->next_scanline < frame->height) {
|
||||||
uint8_t *ptr = line_buf;
|
u8 *ptr = line_buf;
|
||||||
|
|
||||||
for (unsigned x = 0; x < frame->width; ++x) {
|
for (unsigned x = 0; x < frame->width; ++x) {
|
||||||
// See also: https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-uyvy.html
|
// See also: https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-uyvy.html
|
||||||
const bool is_odd_pixel = x & 1;
|
const bool is_odd_pixel = x & 1;
|
||||||
uint8_t y, u, v;
|
u8 y, u, v;
|
||||||
if (frame->format == V4L2_PIX_FMT_YUYV) {
|
if (frame->format == V4L2_PIX_FMT_YUYV) {
|
||||||
y = data[is_odd_pixel ? 2 : 0];
|
y = data[is_odd_pixel ? 2 : 0];
|
||||||
u = data[1];
|
u = data[1];
|
||||||
@@ -167,21 +172,76 @@ static void _jpeg_write_scanlines_yuv(struct jpeg_compress_struct *jpeg, const u
|
|||||||
free(line_buf);
|
free(line_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
uint8_t *line_buf;
|
u8 *line_buf;
|
||||||
US_CALLOC(line_buf, frame->width * 3);
|
US_CALLOC(line_buf, frame->width * 3);
|
||||||
|
|
||||||
const unsigned padding = us_frame_get_padding(frame);
|
const unsigned padding = us_frame_get_padding(frame);
|
||||||
const uint8_t *data = frame->data;
|
const uint image_size = frame->width * frame->height;
|
||||||
|
const uint chroma_array_size = (frame->used - image_size) / 2;
|
||||||
|
const uint chroma_matrix_order = (image_size / chroma_array_size) == 16 ? 4 : 2;
|
||||||
|
US_LOG_DEBUG("Planar data: Image Size %u, Chroma Array Size %u, Chroma Matrix Order %u", image_size, chroma_array_size, chroma_matrix_order);
|
||||||
|
const u8 *data = frame->data;
|
||||||
|
const u8 *chroma1_data = frame->data + image_size;
|
||||||
|
const u8 *chroma2_data = frame->data + image_size + chroma_array_size;
|
||||||
|
|
||||||
while (jpeg->next_scanline < frame->height) {
|
while (jpeg->next_scanline < frame->height) {
|
||||||
uint8_t *ptr = line_buf;
|
u8 *ptr = line_buf;
|
||||||
|
|
||||||
|
for (unsigned x = 0; x < frame->width; ++x) {
|
||||||
|
// See also: https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-yuv420.html
|
||||||
|
u8 y = data[x], u, v;
|
||||||
|
uint chroma_position = x / chroma_matrix_order;
|
||||||
|
|
||||||
|
switch (frame->format) {
|
||||||
|
case V4L2_PIX_FMT_YUV420:
|
||||||
|
u = chroma1_data[chroma_position];
|
||||||
|
v = chroma2_data[chroma_position];
|
||||||
|
break;
|
||||||
|
case V4L2_PIX_FMT_YVU420:
|
||||||
|
u = chroma2_data[chroma_position];
|
||||||
|
v = chroma1_data[chroma_position];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0 && "Unsupported pixel format");
|
||||||
|
return; // Makes linter happy
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr[0] = y;
|
||||||
|
ptr[1] = u;
|
||||||
|
ptr[2] = v;
|
||||||
|
ptr += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += frame->width + padding;
|
||||||
|
|
||||||
|
if( jpeg->next_scanline > 0 && jpeg->next_scanline % chroma_matrix_order == 0 ){
|
||||||
|
chroma1_data += (frame->width + padding) / chroma_matrix_order;
|
||||||
|
chroma2_data += (frame->width + padding) / chroma_matrix_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAMPROW scanlines[1] = {line_buf};
|
||||||
|
jpeg_write_scanlines(jpeg, scanlines, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
|
u8 *line_buf;
|
||||||
|
US_CALLOC(line_buf, frame->width * 3);
|
||||||
|
|
||||||
|
const unsigned padding = us_frame_get_padding(frame);
|
||||||
|
const u8 *data = frame->data;
|
||||||
|
|
||||||
|
while (jpeg->next_scanline < frame->height) {
|
||||||
|
u8 *ptr = line_buf;
|
||||||
|
|
||||||
for (unsigned x = 0; x < frame->width; ++x) {
|
for (unsigned x = 0; x < frame->width; ++x) {
|
||||||
const unsigned int two_byte = (data[1] << 8) + data[0];
|
const unsigned int two_byte = (data[1] << 8) + data[0];
|
||||||
|
|
||||||
ptr[0] = data[1] & 248; // Red
|
ptr[0] = data[1] & 248; // Red
|
||||||
ptr[1] = (uint8_t)((two_byte & 2016) >> 3); // Green
|
ptr[1] = (u8)((two_byte & 2016) >> 3); // Green
|
||||||
ptr[2] = (data[0] & 31) * 8; // Blue
|
ptr[2] = (data[0] & 31) * 8; // Blue
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
|
|
||||||
@@ -198,7 +258,7 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons
|
|||||||
|
|
||||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
const unsigned padding = us_frame_get_padding(frame);
|
const unsigned padding = us_frame_get_padding(frame);
|
||||||
uint8_t *data = frame->data;
|
u8 *data = frame->data;
|
||||||
|
|
||||||
while (jpeg->next_scanline < frame->height) {
|
while (jpeg->next_scanline < frame->height) {
|
||||||
JSAMPROW scanlines[1] = {data};
|
JSAMPROW scanlines[1] = {data};
|
||||||
@@ -210,14 +270,14 @@ static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const
|
|||||||
|
|
||||||
#ifndef JCS_EXTENSIONS
|
#ifndef JCS_EXTENSIONS
|
||||||
static void _jpeg_write_scanlines_bgr24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
static void _jpeg_write_scanlines_bgr24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
|
||||||
uint8_t *line_buf;
|
u8 *line_buf;
|
||||||
US_CALLOC(line_buf, frame->width * 3);
|
US_CALLOC(line_buf, frame->width * 3);
|
||||||
|
|
||||||
const unsigned padding = us_frame_get_padding(frame);
|
const unsigned padding = us_frame_get_padding(frame);
|
||||||
uint8_t *data = frame->data;
|
u8 *data = frame->data;
|
||||||
|
|
||||||
while (jpeg->next_scanline < frame->height) {
|
while (jpeg->next_scanline < frame->height) {
|
||||||
uint8_t *ptr = line_buf;
|
u8 *ptr = line_buf;
|
||||||
|
|
||||||
// swap B and R values
|
// swap B and R values
|
||||||
for (unsigned x = 0; x < frame->width * 3; x += 3) {
|
for (unsigned x = 0; x < frame->width * 3; x += 3) {
|
||||||
|
|||||||
Reference in New Issue
Block a user