Skip to content

Commit

Permalink
Add support for "unicam image" RPi HDMI capture card and UYVY format
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev committed Nov 24, 2024
1 parent cfefbc5 commit 35f59e9
Show file tree
Hide file tree
Showing 17 changed files with 144 additions and 50 deletions.
9 changes: 9 additions & 0 deletions include/utils/PixelFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

enum class PixelFormat {
YUYV,
UYVY,
RGB24,
XRGB,
I420,
Expand All @@ -23,6 +24,10 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat)
{
return PixelFormat::YUYV;
}
else if (format.compare("uyvy") == 0)
{
return PixelFormat::UYVY;
}
else if (format.compare("rgb24") == 0)
{
return PixelFormat::RGB24;
Expand Down Expand Up @@ -58,6 +63,10 @@ inline QString pixelFormatToString(const PixelFormat& pixelFormat)
{
return "yuyv";
}
else if (pixelFormat == PixelFormat::UYVY)
{
return "uyvy";
}
else if (pixelFormat == PixelFormat::RGB24)
{
return "rgb24";
Expand Down
50 changes: 45 additions & 5 deletions sources/grabber/linux/v4l2/V4L2Grabber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,13 @@ namespace
static const V4L2Grabber::HyperHdrFormat supportedFormats[] =
{
{ V4L2_PIX_FMT_YUYV, PixelFormat::YUYV },
{ V4L2_PIX_FMT_UYVY, PixelFormat::UYVY },
{ V4L2_PIX_FMT_XRGB32, PixelFormat::XRGB },
{ V4L2_PIX_FMT_RGB24, PixelFormat::RGB24 },
{ V4L2_PIX_FMT_YUV420, PixelFormat::I420 },
{ V4L2_PIX_FMT_NV12, PixelFormat::NV12 },
{ V4L2_PIX_FMT_MJPEG, PixelFormat::MJPEG },
{ V4L2_PIX_FMT_P010, PixelFormat::P010 }
{ V4L2_PIX_FMT_P010, PixelFormat::P010 }
};


Expand Down Expand Up @@ -147,7 +148,7 @@ void V4L2Grabber::setHdrToneMappingEnabled(int mode)
{
Debug(_log, "setHdrToneMappingMode replacing LUT and restarting");
_V4L2WorkerManager.Stop();
if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12)
if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::UYVY) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12)
|| (_actualVideoFormat == PixelFormat::P010) || (_actualVideoFormat == PixelFormat::MJPEG))
loadLutFile(PixelFormat::YUYV);
else
Expand Down Expand Up @@ -577,6 +578,28 @@ void V4L2Grabber::enumerateV4L2devices(bool silent)
}
}

if (properties.valid.size() == 0 && devName == "/dev/video0")
{
DevicePropertiesItem di;
di.x = fmt.fmt.pix.width;
di.y = fmt.fmt.pix.height;
di.fps = 0;
di.pf = identifyFormat(fmt.fmt.pix.pixelformat);
di.v4l2PixelFormat = fmt.fmt.pix.pixelformat;
di.input = inputIndex;

QString pixelFormat = pixelFormatToString(di.pf);
if (di.pf == PixelFormat::NO_CHANGE)
{
Debug(_log, "%s %d x %d @ %d fps %s (unsupported)", QSTRING_CSTR(properties.name), di.x, di.y, di.fps, QSTRING_CSTR(pixelFormat));
}
else
{
Debug(_log, "%s %d x %d @ %d fps %s, input = %i (seems supported, device not fully compatible with v4l2 grabber model, frame rate is unknown)", QSTRING_CSTR(properties.name), di.x, di.y, di.fps, QSTRING_CSTR(pixelFormat), di.input);
properties.valid.append(di);
}
}

_deviceProperties.insert(realName, properties);

if (!silent)
Expand Down Expand Up @@ -904,7 +927,7 @@ bool V4L2Grabber::init_device(QString selectedDeviceName, DevicePropertiesItem p

streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// Check that the driver knows about framerate get/set
if (xioctl(VIDIOC_G_PARM, &streamparms) >= 0)
if (props.fps > 0 && xioctl(VIDIOC_G_PARM, &streamparms) >= 0)
{
// Check if the device is able to accept a capture framerate set.
if (streamparms.parm.capture.capability == V4L2_CAP_TIMEPERFRAME)
Expand All @@ -918,6 +941,13 @@ bool V4L2Grabber::init_device(QString selectedDeviceName, DevicePropertiesItem p
Info(_log, "Set framerate to %d FPS", streamparms.parm.capture.timeperframe.denominator);
}
}
else
{
if (props.fps == 0)
Warning(_log, "The device doesnt report frame rate settings");
else
Error(_log, "The device doesnt support VIDIOC_G_PARM for frame rate settings");
}

// set the line length
_lineLength = fmt.fmt.pix.bytesperline;
Expand Down Expand Up @@ -974,6 +1004,15 @@ bool V4L2Grabber::init_device(QString selectedDeviceName, DevicePropertiesItem p
}
break;

case V4L2_PIX_FMT_UYVY:
{
loadLutFile(PixelFormat::YUYV);
_actualVideoFormat = PixelFormat::UYVY;
_frameByteSize = props.x * props.y * 2;
Info(_log, "Video pixel format is set to: UYVY");
}
break;

case V4L2_PIX_FMT_XRGB32:
{
loadLutFile(PixelFormat::RGB24);
Expand Down Expand Up @@ -1209,10 +1248,11 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer,
{
V4L2Worker* _workerThread = _V4L2WorkerManager.workers[i];

if ((_actualVideoFormat == PixelFormat::YUYV || _actualVideoFormat == PixelFormat::I420 ||
if ((_actualVideoFormat == PixelFormat::YUYV || _actualVideoFormat == PixelFormat::UYVY || _actualVideoFormat == PixelFormat::I420 ||
_actualVideoFormat == PixelFormat::NV12 || _hdrToneMappingEnabled) && !_lutBufferInit)
{
if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::I420) || (_actualVideoFormat == PixelFormat::NV12) || (_actualVideoFormat == PixelFormat::MJPEG))
if ((_actualVideoFormat == PixelFormat::YUYV) || (_actualVideoFormat == PixelFormat::UYVY) || (_actualVideoFormat == PixelFormat::I420) ||
(_actualVideoFormat == PixelFormat::NV12) || (_actualVideoFormat == PixelFormat::MJPEG))
{
loadLutFile(PixelFormat::YUYV, true);
}
Expand Down
53 changes: 49 additions & 4 deletions sources/utils/FrameDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ void FrameDecoder::processImage(
uint8_t buffer[8];

// validate format
if (pixelFormat != PixelFormat::YUYV &&
if (pixelFormat != PixelFormat::YUYV && pixelFormat != PixelFormat::UYVY &&
pixelFormat != PixelFormat::XRGB && pixelFormat != PixelFormat::RGB24 &&
pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::P010 && pixelFormat != PixelFormat::MJPEG)
{
Expand All @@ -200,7 +200,7 @@ void FrameDecoder::processImage(
}

// validate format LUT
if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::I420 || pixelFormat == PixelFormat::MJPEG ||
if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::UYVY || pixelFormat == PixelFormat::I420 || pixelFormat == PixelFormat::MJPEG ||
pixelFormat == PixelFormat::NV12 || pixelFormat == PixelFormat::P010) && lutBuffer == NULL)
{
Error(Logger::getInstance("FrameDecoder"), "Missing LUT table for YUV colorspace");
Expand Down Expand Up @@ -248,6 +248,30 @@ void FrameDecoder::processImage(
return;
}

if (pixelFormat == PixelFormat::UYVY)
{
for (int yDest = 0, ySource = _cropTop; yDest < outputHeight; ++ySource, ++yDest)
{
uint8_t* currentDest = destMemory + ((uint64_t)destLineSize) * yDest;
uint8_t* endDest = currentDest + destLineSize;
uint8_t* currentSource = (uint8_t*)data + (((uint64_t)lineLength * ySource) + (((uint64_t)_cropLeft) << 1));

while (currentDest < endDest)
{
*((uint32_t*)&buffer) = *((uint32_t*)currentSource);

ind_lutd = LUT_INDEX(buffer[1], buffer[0], buffer[2]);
ind_lutd2 = LUT_INDEX(buffer[3], buffer[0], buffer[2]);

*((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd]));
currentDest += 3;
*((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd2]));
currentDest += 3;
currentSource += 4;
}
}
return;
}

if (pixelFormat == PixelFormat::RGB24)
{
Expand Down Expand Up @@ -474,7 +498,7 @@ void FrameDecoder::processQImage(
uint8_t buffer[8];

// validate format
if (pixelFormat != PixelFormat::YUYV &&
if (pixelFormat != PixelFormat::YUYV && pixelFormat != PixelFormat::UYVY &&
pixelFormat != PixelFormat::XRGB && pixelFormat != PixelFormat::RGB24 &&
pixelFormat != PixelFormat::I420 && pixelFormat != PixelFormat::NV12 && pixelFormat != PixelFormat::P010)
{
Expand All @@ -483,7 +507,7 @@ void FrameDecoder::processQImage(
}

// validate format LUT
if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::I420 ||
if ((pixelFormat == PixelFormat::YUYV || pixelFormat == PixelFormat::UYVY || pixelFormat == PixelFormat::I420 ||
pixelFormat == PixelFormat::NV12 || pixelFormat == PixelFormat::P010) && lutBuffer == NULL)
{
Error(Logger::getInstance("FrameDecoder"), "Missing LUT table for YUV colorspace");
Expand Down Expand Up @@ -522,6 +546,27 @@ void FrameDecoder::processQImage(
return;
}

if (pixelFormat == PixelFormat::UYVY)
{
for (int yDest = 0, ySource = 0; yDest < outputHeight; ySource += 2, ++yDest)
{
uint8_t* currentDest = destMemory + ((uint64_t)destLineSize) * yDest;
uint8_t* endDest = currentDest + destLineSize;
uint8_t* currentSource = (uint8_t*)data + (((uint64_t)lineLength * ySource));

while (currentDest < endDest)
{
*((uint32_t*)&buffer) = *((uint32_t*)currentSource);

ind_lutd = LUT_INDEX(buffer[1], buffer[0], buffer[2]);

*((uint32_t*)currentDest) = *((uint32_t*)(&lutBuffer[ind_lutd]));
currentDest += 3;
currentSource += 4;
}
}
return;
}

if (pixelFormat == PixelFormat::RGB24)
{
Expand Down
6 changes: 3 additions & 3 deletions www/i18n/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1246,10 +1246,10 @@
"option_calibration_intro": "Vyberte typ kalibrace",
"option_calibration_video": "Kalibrace pomocí testovacího videa přehrávaného vaším oblíbeným přehrávačem videa.<br/>Kalibrujeme LUT s ohledem na grabber, přehrávač a váš televizor.",
"option_calibration_classic": "Kalibrace pomocí systému Windows s povoleným režimem HDR a webovým prohlížečem.<br/>Kalibrujeme LUT s ohledem na grabber a váš televizor.",
"video_calibration_overview": "<b>1</b> Musíte nastavit formát videa svého grabberu na MJPEG/YUV/NV12. Jiné formáty nejsou podporovány.<br/><br/><b>2</b> Pokud provádíte kalibraci pomocí Flatbufferů, musíte v jeho nastavení povolit mapování tónů. Podporován je pouze formát videa NV12.</br><br/><b>3</b> Testovací soubory si můžete stáhnout zde: <a href='https://github.com/awawa-dev/awawa- dev.github.io/tree/master/calibration'>odkaz</a>. V přehrávači spusťte přehrávání testovacího souboru. Měli byste to vidět v náhledu videa HyperHDR. Testovací obrazovka musí zabírat celou obrazovku a nesmí být viditelné žádné nadbytečné prvky, jako je nabídka přehrávače.</br><br/><b>4</b> Pro kalibraci byste měli vybrat soubor s ' hdr' v názvu, pokud váš systém nebo přehrávač automaticky nepoužívá mapování tónů SDR na HDR nebo pokud neplánujete kalibrovat signál SDR. V takovém případě, abyste se přizpůsobili takovému scénáři, vyberte soubor s 'sdr' v názvu.</br><br/><b>5</b> Formát YUV420 s omezeným rozsahem poskytuje největší kompatibilitu s průměrem až velmi dobrá kvalita a je nejoblíbenější. Formát YUV444 poskytuje nejlepší kvalitu, ale je vzácné najít materiály zakódované v této podobě.</br><br/><b>6</b> <b>Pokud se kalibrují pomocí Windows 11 (pomocí webového prohlížeče nebo přehrávače videa jako zdroje videa), vypněte funkce, jako je „Noční světlo“, „Automatická správa barev pro aplikace“ a „Auto-HDR“. Neměňte vyvážení barev v ovladači grafiky. Výstup GFX by měl podporovat např. 10 nebo 12 bit RGB v plném rozsahu PC.</b></br><br/><b>7</b> <b>Výpočty jsou velmi intenzivní a zatěžují vaše zařízení! <svg data-src='svg/performance_undervoltage.svg' class='svg4hyperhdr ms-1'></svg></b>Můžete deaktivovat korekci barev LCH, abyste trochu snížili zátěž.",
"video_calibration_overview": "<b>1</b> Musíte nastavit formát videa svého grabberu na MJPEG/YUV/NV12/P010. Jiné formáty nejsou podporovány.<br/><br/><b>2</b> Pokud provádíte kalibraci pomocí Flatbufferů, musíte v jeho nastavení povolit mapování tónů. Podporován je pouze formát videa NV12.</br><br/><b>3</b> Testovací soubory si můžete stáhnout zde: <a href='https://github.com/awawa-dev/awawa- dev.github.io/tree/master/calibration'>odkaz</a>. V přehrávači spusťte přehrávání testovacího souboru. Měli byste to vidět v náhledu videa HyperHDR. Testovací obrazovka musí zabírat celou obrazovku a nesmí být viditelné žádné nadbytečné prvky, jako je nabídka přehrávače.</br><br/><b>4</b> Pro kalibraci byste měli vybrat soubor s ' hdr' v názvu, pokud váš systém nebo přehrávač automaticky nepoužívá mapování tónů SDR na HDR nebo pokud neplánujete kalibrovat signál SDR. V takovém případě, abyste se přizpůsobili takovému scénáři, vyberte soubor s 'sdr' v názvu.</br><br/><b>5</b> Formát YUV420 s omezeným rozsahem poskytuje největší kompatibilitu s průměrem až velmi dobrá kvalita a je nejoblíbenější. Formát YUV444 poskytuje nejlepší kvalitu, ale je vzácné najít materiály zakódované v této podobě.</br><br/><b>6</b> <b>Pokud se kalibrují pomocí Windows 11 (pomocí webového prohlížeče nebo přehrávače videa jako zdroje videa), vypněte funkce, jako je „Noční světlo“, „Automatická správa barev pro aplikace“ a „Auto-HDR“. Neměňte vyvážení barev v ovladači grafiky. Výstup GFX by měl podporovat např. 10 nebo 12 bit RGB v plném rozsahu PC.</b></br><br/><b>7</b> <b>Výpočty jsou velmi intenzivní a zatěžují vaše zařízení! <svg data-src='svg/performance_undervoltage.svg' class='svg4hyperhdr ms-1'></svg></b>Můžete deaktivovat korekci barev LCH, abyste trochu snížili zátěž.",
"chk_calibration_debug": "Ladit",
"flatbuffers_nv12_quarter_of_frame_title": "Čtvrtina rámu pro NV12",
"flatbuffers_nv12_quarter_of_frame_expl": "Kodek NV12 obsahuje čtyřikrát více informací o jasu než o barvě. Tato možnost umožňuje snížit zatížení procesoru snížením výšky a šířky snímku videa o 2, aniž by došlo ke ztrátě informací o barvách.",
"chk_lchCorrection": "Korekce barev LCH",
"grabber_calibration_expl": "Tento nástroj vám umožňuje vytvořit novou kalibrovanou HDR LUT pro váš grabber (nebo externí zdroj flatbufferů) co nejblíže skutečným vstupním barvám.<br/>Potřebujete zdroj videa HDR10, který dokáže zobrazit tuto webovou stránku, například: Windows 10 s povoleným HDR ve vlastnostech grafického ovladače.<br/>Během kalibrace může obrazovka blikat. Tento proces obvykle trvá přibližně několik minut na počítači Intel 7 Windows (v závislosti na zdrojích hostitelského procesoru a snímkové rychlosti zachycování videa).<br/><b>Výpočty jsou velmi intenzivní a zatěžují vaše zařízení <svg data- src='svg/performance_undervoltage.svg' class='svg4hyperhdr ms-1'></svg></b>Můžete deaktivovat korekci barev LCH, abyste trochu snížili zátěž<br/>Postup můžete sledovat v protokolech HyperHDR pomocí prohlížeče z jiného zařízení.<br/><br/><br/><b>1</b> Pokud je vše správně připojeno, měla by se tato stránka zobrazit na televizní obrazovce (jako obsah HDR) a živý náhled v HyperHDR (snímáno grabberem).</br><b>2</b> Absolutní minimální rozlišení snímání je 1280x720 (toto ověříme). Doporučeno je 1920x1080 YUV/NV12. Poměr stran 1920/1080 musí být zachován.<br/><b>3</b> Pokud je povolen, musíte ve vlastnostech grabberu deaktivovat 'Režim čtvrtiny snímku'.<br/><b>4</b> Vy musí nastavit formát videa grabberu na MJPEG/YUV/NV12.<br/><b>5</b> Před spuštěním procesu přepněte svůj WWW prohlížeč do režimu celé obrazovky (klávesa F11, ověříme to) .</br><b>6</b> <b>Pokud provádíte kalibraci pomocí Windows 11, vypněte funkce, jako je „Noční světlo“, „Automatická správa barev pro aplikace“ a „Automatické HDR“. Neměňte vyvážení barev v ovladači grafiky. Výstup GFX by měl podporovat např. 10 nebo 12 bit RGB v plném rozsahu PC.</b><br/><br/>Po dokončení kalibrace bude v domovském adresáři uživatele HyperHDR vytvořen nový soubor tabulky LUT (lut_lin_tables.3d) a je okamžitě připraven použít, když pouze povolíte mapování tónů HDR. Podrobnosti naleznete v protokolech HyperHDR."
}
"grabber_calibration_expl": "Tento nástroj vám umožňuje vytvořit novou kalibrovanou HDR LUT pro váš grabber (nebo externí zdroj flatbufferů) co nejblíže skutečným vstupním barvám.<br/>Potřebujete zdroj videa HDR10, který dokáže zobrazit tuto webovou stránku, například: Windows 10 s povoleným HDR ve vlastnostech grafického ovladače.<br/>Během kalibrace může obrazovka blikat. Tento proces obvykle trvá přibližně několik minut na počítači Intel 7 Windows (v závislosti na zdrojích hostitelského procesoru a snímkové rychlosti zachycování videa).<br/><b>Výpočty jsou velmi intenzivní a zatěžují vaše zařízení <svg data- src='svg/performance_undervoltage.svg' class='svg4hyperhdr ms-1'></svg></b>Můžete deaktivovat korekci barev LCH, abyste trochu snížili zátěž<br/>Postup můžete sledovat v protokolech HyperHDR pomocí prohlížeče z jiného zařízení.<br/><br/><br/><b>1</b> Pokud je vše správně připojeno, měla by se tato stránka zobrazit na televizní obrazovce (jako obsah HDR) a živý náhled v HyperHDR (snímáno grabberem).</br><b>2</b> Absolutní minimální rozlišení snímání je 1280x720 (toto ověříme). Doporučeno je 1920x1080 YUV/NV12. Poměr stran 1920/1080 musí být zachován.<br/><b>3</b> Pokud je povolen, musíte ve vlastnostech grabberu deaktivovat 'Režim čtvrtiny snímku'.<br/><b>4</b> Vy musí nastavit formát videa grabberu na MJPEG/YUV/NV12/P010.<br/><b>5</b> Před spuštěním procesu přepněte svůj WWW prohlížeč do režimu celé obrazovky (klávesa F11, ověříme to) .</br><b>6</b> <b>Pokud provádíte kalibraci pomocí Windows 11, vypněte funkce, jako je „Noční světlo“, „Automatická správa barev pro aplikace“ a „Automatické HDR“. Neměňte vyvážení barev v ovladači grafiky. Výstup GFX by měl podporovat např. 10 nebo 12 bit RGB v plném rozsahu PC.</b><br/><br/>Po dokončení kalibrace bude v domovském adresáři uživatele HyperHDR vytvořen nový soubor tabulky LUT (lut_lin_tables.3d) a je okamžitě připraven použít, když pouze povolíte mapování tónů HDR. Podrobnosti naleznete v protokolech HyperHDR."
}
Loading

0 comments on commit 35f59e9

Please sign in to comment.