Support NV12 format on Flatbuffer

This commit is contained in:
LordGrey 2024-12-01 15:04:06 +01:00
parent 27f74af4e3
commit adfe039feb
7 changed files with 161 additions and 56 deletions

View File

@ -71,5 +71,7 @@ private:
quint16 _port; quint16 _port;
const QJsonDocument _config; const QJsonDocument _config;
int _pixelDecimation;
QVector<FlatBufferClient*> _openConnections; QVector<FlatBufferClient*> _openConnections;
}; };

View File

@ -13,6 +13,7 @@ public:
void setHorizontalPixelDecimation(int decimator) { _horizontalDecimation = decimator; } void setHorizontalPixelDecimation(int decimator) { _horizontalDecimation = decimator; }
void setVerticalPixelDecimation(int decimator) { _verticalDecimation = decimator; } void setVerticalPixelDecimation(int decimator) { _verticalDecimation = decimator; }
void setPixelDecimation(int decimator) { _horizontalDecimation = decimator; _verticalDecimation = decimator;}
void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom); void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom);
void setVideoMode(VideoMode mode) { _videoMode = mode; } void setVideoMode(VideoMode mode) { _videoMode = mode; }
void setFlipMode(FlipMode mode) { _flipMode = mode; } void setFlipMode(FlipMode mode) { _flipMode = mode; }

View File

@ -1,4 +1,5 @@
#include "FlatBufferClient.h" #include "FlatBufferClient.h"
#include <utils/PixelFormat.h>
// qt // qt
#include <QTcpSocket> #include <QTcpSocket>
@ -15,6 +16,8 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, int timeout, QObject *par
, _timeout(timeout * 1000) , _timeout(timeout * 1000)
, _priority() , _priority()
{ {
_imageResampler.setPixelDecimation(1);
// timer setup // timer setup
_timeoutTimer->setSingleShot(true); _timeoutTimer->setSingleShot(true);
_timeoutTimer->setInterval(_timeout); _timeoutTimer->setInterval(_timeout);
@ -25,6 +28,11 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, int timeout, QObject *par
connect(_socket, &QTcpSocket::disconnected, this, &FlatBufferClient::disconnected); connect(_socket, &QTcpSocket::disconnected, this, &FlatBufferClient::disconnected);
} }
void FlatBufferClient::setPixelDecimation(int decimator)
{
_imageResampler.setPixelDecimation(decimator);
}
void FlatBufferClient::readyRead() void FlatBufferClient::readyRead()
{ {
_timeoutTimer->start(); _timeoutTimer->start();
@ -141,55 +149,71 @@ void FlatBufferClient::handleRegisterCommand(const hyperionnet::Register *regReq
void FlatBufferClient::handleImageCommand(const hyperionnet::Image *image) void FlatBufferClient::handleImageCommand(const hyperionnet::Image *image)
{ {
Image<ColorRgb> imageRGB;
// extract parameters // extract parameters
int duration = image->duration(); int duration = image->duration();
const void* reqPtr; const void* reqPtr;
if ((reqPtr = image->data_as_RawImage()) != nullptr) if ((reqPtr = image->data_as_RawImage()) != nullptr)
{ {
const auto *img = static_cast<const hyperionnet::RawImage*>(reqPtr); const auto* img = static_cast<const hyperionnet::RawImage*>(reqPtr);
const auto & imageData = img->data();
const int width = img->width();
const int height = img->height();
if (width <= 0 || height <= 0) hyperionnet::RawImageT rawImageNative;
img->UnPackTo(&rawImageNative);
const int width = rawImageNative.width;
const int height = rawImageNative.height;
if (width <= 0 || height <= 0 || rawImageNative.data.empty())
{ {
sendErrorReply("Size of image data does not match with the width and height"); sendErrorReply("Invalid width and/or height or no raw image data provided");
return; return;
} }
// check consistency of the size of the received data // check consistency of the size of the received data
int channelCount = (int)imageData->size()/(width*height); int bytesPerPixel = rawImageNative.data.size() / (width * height);
if (channelCount != 3 && channelCount != 4) if (bytesPerPixel != 3 && bytesPerPixel != 4)
{ {
sendErrorReply("Size of image data does not match with the width and height"); sendErrorReply("Size of image data does not match with the width and height");
return; return;
} }
// create ImageRgb imageRGB.resize(width, height);
Image<ColorRgb> imageRGB(width, height); processRawImage(rawImageNative, bytesPerPixel, _imageResampler, imageRGB);
if (channelCount == 3)
{
memmove(imageRGB.memptr(), imageData->data(), imageData->size());
}
if (channelCount == 4)
{
for (int source=0, destination=0; source < width * height * static_cast<int>(sizeof(ColorRgb)); source+=sizeof(ColorRgb), destination+=sizeof(ColorRgba))
{
memmove((uint8_t*)imageRGB.memptr() + source, imageData->data() + destination, sizeof(ColorRgb));
}
}
emit setGlobalInputImage(_priority, imageRGB, duration);
emit setBufferImage("FlatBuffer", imageRGB);
} }
else if ((reqPtr = image->data_as_NV12Image()) != nullptr)
{
const auto* img = static_cast<const hyperionnet::NV12Image*>(reqPtr);
hyperionnet::NV12ImageT nv12ImageNative;
img->UnPackTo(&nv12ImageNative);
const int width = nv12ImageNative.width;
const int height = nv12ImageNative.height;
if (width <= 0 || height <= 0 || nv12ImageNative.data_y.empty() || nv12ImageNative.data_uv.empty())
{
sendErrorReply("Invalid width and/or height or no complete NV12 image data provided");
return;
}
imageRGB.resize(width, height);
processNV12Image(nv12ImageNative, _imageResampler, imageRGB);
}
else
{
sendErrorReply("No or unknown image data provided");
return;
}
emit setGlobalInputImage(_priority, imageRGB, duration);
emit setBufferImage("FlatBuffer", imageRGB);
// send reply // send reply
sendSuccessReply(); sendSuccessReply();
} }
void FlatBufferClient::handleClearCommand(const hyperionnet::Clear *clear) void FlatBufferClient::handleClearCommand(const hyperionnet::Clear *clear)
{ {
// extract parameters // extract parameters
@ -242,3 +266,50 @@ void FlatBufferClient::sendErrorReply(const std::string &error)
_builder.Clear(); _builder.Clear();
} }
inline void FlatBufferClient::processRawImage(const hyperionnet::RawImageT& raw_image, int bytesPerPixel, ImageResampler& resampler, Image<ColorRgb>& outputImage) {
int width = raw_image.width;
int height = raw_image.height;
int lineLength = width * bytesPerPixel;
PixelFormat pixelFormat = (bytesPerPixel == 4) ? PixelFormat::RGB32 : PixelFormat::RGB24;
// Process the image
resampler.processImage(
raw_image.data.data(), // Raw RGB/RGBA buffer
width, // Image width
height, // Image height
lineLength, // Line length
pixelFormat, // Pixel format (RGB24/RGB32)
outputImage // Output image
);
}
inline void FlatBufferClient::processNV12Image(const hyperionnet::NV12ImageT& nv12_image, ImageResampler& resampler, Image<ColorRgb>& outputImage) {
// Combine data_y and data_uv into a single buffer
int width = nv12_image.width;
int height = nv12_image.height;
size_t y_size = nv12_image.data_y.size();
size_t uv_size = nv12_image.data_uv.size();
std::vector<uint8_t> combined_buffer(y_size + uv_size);
std::memcpy(combined_buffer.data(), nv12_image.data_y.data(), y_size);
std::memcpy(combined_buffer.data() + y_size, nv12_image.data_uv.data(), uv_size);
// Determine line length (stride_y)
int lineLength = nv12_image.stride_y > 0 ? nv12_image.stride_y : width;
PixelFormat pixelFormat = PixelFormat::NV12;
// Process the image
resampler.processImage(
combined_buffer.data(), // Combined NV12 buffer
width, // Image width
height, // Image height
lineLength, // Line length for Y plane
pixelFormat, // Pixel format (NV12)
outputImage // Output image
);
}

View File

@ -6,6 +6,7 @@
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <utils/ColorRgba.h> #include <utils/ColorRgba.h>
#include <utils/Components.h> #include <utils/Components.h>
#include "utils/ImageResampler.h"
// flatbuffer FBS // flatbuffer FBS
#include "hyperion_reply_generated.h" #include "hyperion_reply_generated.h"
@ -33,6 +34,8 @@ public:
/// ///
explicit FlatBufferClient(QTcpSocket* socket, int timeout, QObject *parent = nullptr); explicit FlatBufferClient(QTcpSocket* socket, int timeout, QObject *parent = nullptr);
void setPixelDecimation(int decimator);
signals: signals:
/// ///
/// @brief forward register data to HyperionDaemon /// @brief forward register data to HyperionDaemon
@ -138,6 +141,9 @@ private:
/// ///
void sendErrorReply(const std::string & error); void sendErrorReply(const std::string & error);
void processRawImage(const hyperionnet::RawImageT& raw_image, int bytesPerPixel, ImageResampler& resampler, Image<ColorRgb>& outputImage);
void processNV12Image(const hyperionnet::NV12ImageT& nv12_image, ImageResampler& resampler, Image<ColorRgb>& outputImage);
private: private:
Logger *_log; Logger *_log;
QTcpSocket *_socket; QTcpSocket *_socket;
@ -148,6 +154,8 @@ private:
QByteArray _receiveBuffer; QByteArray _receiveBuffer;
ImageResampler _imageResampler;
// Flatbuffers builder // Flatbuffers builder
flatbuffers::FlatBufferBuilder _builder; flatbuffers::FlatBufferBuilder _builder;
}; };

View File

@ -62,6 +62,12 @@ void FlatBufferServer::handleSettingsUpdate(settings::type type, const QJsonDocu
_timeout = obj["timeout"].toInt(5000); _timeout = obj["timeout"].toInt(5000);
// enable check // enable check
obj["enable"].toBool(true) ? startServer() : stopServer(); obj["enable"].toBool(true) ? startServer() : stopServer();
_pixelDecimation = obj["pixelDecimation"].toInt(1);
for (const auto& client : _openConnections)
{
client->setPixelDecimation(_pixelDecimation);
}
} }
} }
@ -75,6 +81,9 @@ void FlatBufferServer::newConnection()
{ {
Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString()));
FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this);
client->setPixelDecimation(_pixelDecimation);
// internal // internal
connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected);
connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput);

View File

@ -12,9 +12,17 @@ table RawImage {
height:int = -1; height:int = -1;
} }
union ImageType {RawImage} table NV12Image {
data_y:[ubyte];
data_uv:[ubyte];
width:int;
height:int;
stride_y:int = 0;
stride_uv:int = 0;
}
union ImageType {RawImage, NV12Image}
// Either RGB or RGBA data can be transferred
table Image { table Image {
data:ImageType (required); data:ImageType (required);
duration:int = -1; duration:int = -1;

View File

@ -1,35 +1,41 @@
{ {
"type" : "object", "type" : "object",
"title" : "edt_conf_fbs_heading_title", "title" : "edt_conf_fbs_heading_title",
"properties" : "properties": {
{ "enable": {
"enable" : "type": "boolean",
{ "required": true,
"type" : "boolean", "title": "edt_conf_general_enable_title",
"required" : true, "default": true,
"title" : "edt_conf_general_enable_title", "propertyOrder": 1
"default" : true,
"propertyOrder" : 1
}, },
"port" : "port": {
{ "type": "integer",
"type" : "integer", "required": true,
"required" : true, "title": "edt_conf_general_port_title",
"title" : "edt_conf_general_port_title", "minimum": 1024,
"minimum" : 1024, "maximum": 65535,
"maximum" : 65535, "default": 19400,
"default" : 19400, "propertyOrder": 2
"propertyOrder" : 2
}, },
"timeout" : "timeout": {
{ "type": "integer",
"type" : "integer", "required": true,
"required" : true, "title": "edt_conf_fbs_timeout_title",
"title" : "edt_conf_fbs_timeout_title", "append": "edt_append_s",
"append" : "edt_append_s", "minimum": 1,
"minimum" : 1, "default": 5,
"default" : 5, "propertyOrder": 3
"propertyOrder" : 3 },
"pixelDecimation": {
"type": "integer",
"title": "edt_conf_fg_pixelDecimation_title",
"minimum": 1,
"maximum": 30,
"default": 1,
"required": false,
"access": "advanced",
"propertyOrder": 4
} }
}, },
"additionalProperties" : false "additionalProperties" : false