Po po téměř roce vývoje od vydání verze 5.38 byla vydána nová stabilní verze 5.40 programovacího jazyka Perl (Wikipedie). Do vývoje se zapojilo 75 vývojářů. Změněno bylo přibližně 160 tisíc řádků v 1 500 souborech. Přehled novinek a změn v podrobném seznamu.
Uroš Popović popisuje, jak si nastavit Linux na desce jako Raspberry Pi Zero, aby je šlo používat jako USB „flešku“.
Andreas Kling oznámil, že jelikož už se nevěnuje nezávislému operačnímu systému SerenityOS, ale výhradně jeho webovému prohlížeči Ladybird, přičemž vyvíjí primárně na Linuxu, SerenityOS opustí a Ladybird bude nově samostatný projekt (nový web, repozitář na GitHubu).
Po dvou měsících vývoje byla vydána nová verze 0.13.0 programovacího jazyka Zig (GitHub, Wikipedie). Přispělo 73 vývojářů. Přehled novinek v poznámkách k vydání.
Na čem aktuálně pracují vývojáři GNOME a KDE? Pravidelný přehled novinek v Týden v GNOME a Týden v KDE.
Před 70 lety, 7. června 1954, ve věku 41 let, zemřel Alan Turing, britský matematik, logik, kryptoanalytik a zakladatel moderní informatiky.
NiceGUI umožňuje používat webový prohlížeč jako frontend pro kód v Pythonu. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
Open source platforma Home Assistant (Demo, GitHub, Wikipedie) pro monitorování a řízení inteligentní domácnosti byla vydána ve verzi 2024.6. Z novinek lze vypíchnout lepší integraci LLM (OpenAI, Google AI, Ollama) nebo podporu Matter 1.3.
IKEA ve Spojeném království hledá zaměstnance do své nové pobočky. Do pobočky v počítačové hře Roblox. Nástupní mzda je 13,15 liber na hodinu.
Alyssa Rosenzweig se v příspěvku na svém blogu Vulkan 1.3 na M1 za 1 měsíc rozepsala o novém Vulkan 1.3 ovladači Honeykrisp pro Apple M1 splňujícím specifikaci Khronosu. Vychází z ovladače NVK pro GPU od Nvidie. V plánu je dále rozchodit DXVK a vkd3d-proton a tím pádem Direct3D, aby na Apple M1 s Asahi Linuxem běžely hry pro Microsoft Windows.
Odkazy
V dnešnom blogu sa pozrieme na API rozšírenia X video. Ukážeme si ako je možné použiť volanie XvPutImage pre akcelerované zobrazovanie YUV / RGB pixmapy.
Rozšírenie XVideo (skrátené Xv) bolo navrhnuté pre akcelerované zobrazovanie videa. Xv nie je žiadnou novinkou, jeho aktuálna major verzia bola vydaná už v roku 1991 (tj. v čase písania tohto blogu má vyše 21 rokov).
API je pomerne minimalistické, čo je aj jedným z dôvodov prečo vydržalo tak dlhú dobu bez výraznejších zmien. Autori zrejme neočakávali dlhú životnosť, takže v dokumentácii môžme nájsť vetu: "So, the life expectancy of Xv is not long." API poskytuje najnutnejšie metódy pre určenie počtu a typu adaptrérov, pripojenie sa na výstupný port adaptéru, nastavenie atribútov ako jas, kontrast, saturácia a v neposlednom rade aj pre samotný vstup / výstup obrazových dát.
Pre prezentáciu obrazu na výstupnom zariadení disponuje metódami PutImage, PutStill a PutVideo. V súčasných aplikáciách sa používa len prvá metóda. Zvyšné nemajú zvyčajne žiadnu podporu u ovládačoch.
Spôsob renderovania obrazu závisí od použitého WM. Pri nekompozitnom WM je možné použiť "video overlay" adaptér, kde sa okno renderuje s určitou farbou a samotné video pridáva do výsledného obrazu až grafická karta. S kompozitným WM sa používa "textured video" adaptér, ktorý renderuje video do textúry. Tá sa v kompozitnom WM mapuje cez pixelbuffery na plochu okna. Pri takomto spôsobe renderovania je možné z videa urobiť aj screenshot. Video overlay z princípu svojho fungovania tvorbu screenshotov neumožňuje.
Qt je multiplatformový framework. Pre použitie Xv budeme potrebovať pracovať priamo s X, čo znamená aj porušenie multiplatformovosti. Dúfam, že varovanie je dostatočne odstrašujúce na to, aby sme ho mohli ďalej ignorovať a tváriť sa, že neexistuje
Pri všetkých volaniach budeme potrebovať referenciu na Display
. U Qt 4 to zariadíme includovaním #include <QX11Info>
a zavolaním metódy widget.x11Info().display();
.
U Qt 5 je možné použiť Xlib jedine s xcb backendom. Pre prístup k display-u budeme potrebovať privátne hlavičky. Includujeme teda <QWindow>
a <qpa/qplatformnativeinterface.h>
. Pre Qt 5 bude potrebný nasledujúci kód:
static_cast<Display *>( qGuiApp->platformNativeInterface() ->nativeResourceForWindow("display", new QWindow(/* screen */)) );
Ak si náš widget pre renderovanie Xv nazveme QtXvWidget
bude kód pre získanie referencie na Display
vyzerať nasledovne:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include <QWindow> #include <qpa/qplatformnativeinterface.h> #else #include <QX11Info> #endif #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/extensions/Xv.h> #include <X11/extensions/Xvlib.h> Display *QtXvWidget::getDpy() const { #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) QWindow *window = new QWindow(/* screen */); Display *dpy = static_cast<Display *>(qGuiApp->platformNativeInterface()->nativeResourceForWindow("display", window)); delete window; return dpy; #else return x11Info().display(); #endif }
Pre Qt 5 potrebujeme do ciest kompilátora pridať privátne hlavičky Qt 5. To je možné pridaním gui-private
do .pro
súboru.
QT += core gui multimedia contains(QT_VERSION, ^5\\..*) { QT+= gui-private widgets }
Aby bolo možné vykresľovať na plochu widgetu priamo pomocou Xlib musíme zaistiť, aby Qt widget neprekresľovalo. Na to stačí vypnúť double buffering a vykresľovanie systémového pozada.
widget.setAttribute(Qt::WA_PaintOnScreen, true); widget.setAttribute(Qt::WA_NoSystemBackground, true);
Dostupnosť rozšírenia Xv sa dá preveriť volaním funkcie XvQueryExtension
. Ak je rozšírenie dostupné vráti Success
. Nasledujúci kód vypíše verziu Xv za predpokladu, že je dostupná.
unsigned int version, release, request_base, event_base, error_base; if (XvQueryExtension(getDpy(), &version, &release, &request_base, &event_base, &error_base) == Success) { qDebug() << "X-Video Extension version" << (QString::number(version) + "." + QString::number(release)).toAscii().constData();
Po spustení sa vypíše niečo ako X-Video Extension version 2.2
.
Adaptér je cieľové zariadenie, na ktoré budeme posielať obraz. Jedna grafická karta môže mať aj niekoľko adaptérov. V prípade Intel GMA sú napríklad dostupné 2 adaptéry. Zoznam adaptérov je dostupný cez volanie XvQueryAdaptors
.
Štruktúra XvAdaptorInfo
obsahuje pre každý adaptér jeho názov (name
), typ (type
) adaptéra, číslo prvého portu (base_id
) a počet portov (num_ports
). Jeden adaptér zvyčajne umožňuje vykresľovať viacej okien súčasne pomocou rôznych portov.
Typ adaptéru určuje, či adaptér podporuje zápis (XvInputMask
), čítanie (XvOutputMask
) a typy dát, ktoré môže prijímať (pixmapu - XvImageMask
, video - XvVideoMask
, alebo statický obraz - XvStillMask
).
XvAdaptorInfo *adaptors = 0; unsigned int count = 0; if (XvQueryAdaptors(getDpy(), DefaultRootWindow(getDpy()), &count, &adaptors) == Success) { for (unsigned int i = 0; i < count; ++i) { if ((adaptors[i].type & XvInputMask) && (adaptors[i].type & XvImageMask)) { qDebug() << adaptors[i].name; qDebug() << " Base ID:" << adaptors[i].base_id; qDebug() << " Ports: " << adaptors[i].num_ports; } } XFree(adaptors); }
Výstup pre grafickú kartu Intel GMA:
Intel(R) Video Overlay Base ID: 75 Ports: 1 Intel(R) Textured Video Base ID: 76 Ports: 16
Pri použití Xv musí byť port exkluzívne uzamknutý aplikáciou, ktorá ho používa. Zamykanie sa vykonáva volaním XvGrabPort
a odomykanie volaním XvUngrabPort
. Uzamykanie a odomykanie portu sa musí volať aj s časovou pečiatkou akcie (vystačíme si s použitím CurrentTime
).
XvPortID port = 75; if (XvGrabPort(getDpy(), port, CurrentTime) == Success) { ... } ... // Ukončenie používania portu XvUngrabPort(getDpy(), port, CurrentTime);
Súčasná verzia Xv podporuje 2 farebné priestory. Je to klasické RGB a YUV používané najčastejšie pri prenose obrazu. Výhodou YUV je možnosť redukcie chromatickej zložky bez príliš viditeľného zhoršenia kvality obrazu.
Zoznam podporovaných formátov je dostupný pomocou volania XvListImageFormats
. Štruktúra XvImageFormatValues
obsahuje nasledujúce položky.
Pre všetky typy | |
---|---|
id: int | Unikátny identifikátor formátu v rámci adaptéru. Tento identifikátor sa používa pri komunikácii s adaptérom. |
type: int | Typ formátu, buď XvRGB alebo XvYUV. |
byte_order: int | Spôsob zoradenia bytov: LSBFirst - najmenej významný na začiatku alebo MSBFirst - najvýznamnejší na začiatku. |
guid: char[16] | Globálny identifikátor. |
bits_per_pixel: int | Počet bitov pixelu. |
format: int | Zabalený - XvPacked (jednotlivé komponenty nasledujú v streame za sebou, napr YUYV) alebo planárny - XvPlanar (komponenty sú oddelené, napr. YYYYUUVV). |
num_planes int | Počet komponentov ak sa používa planárny formát |
Pre RGB | |
depth int | Farebná hĺbka. |
red_mask unsigned int | Maska bitov červenej farby napr. 0x00ff0000. |
green_mask unsigned int | Maska bitov zelenej farby napr. 0x0000ff00. |
blue_mask unsigned int | Maska bitov modrej farby napr. 0x000000ff. |
Pre YUV | |
y_sample_bits unsigned int | Počet bitov pre zložku Y (luminancia). |
u_sample_bits unsigned int | Počet bitov pre chromatickú zložku U. |
v_sample_bits unsigned int | Počet bitov pre chromatickú zložku V. |
horz_y_period unsigned int | Horizontálna veľkosť makrobloku Y. |
horz_u_period unsigned int | Horizontálna veľkosť makrobloku U. |
horz_v_period unsigned int | Horizontálna veľkosť makrobloku V. |
vert_y_period unsigned int | Vertikálna veľkosť makrobloku Y. |
vert_u_period unsigned int | Vertikálna veľkosť makrobloku U. |
vert_v_period unsigned int | Vertikálna veľkosť makrobloku V. |
component_order char[32] | Poradie zložiek napr. YUYV. |
scanline_order int | Poradie riadkov v streame, buď zhora nadol - XvTopToBottom alebo zdola nahor - XvBottomToTop. |
Nasledujúcim kódom sa dá vypísať zoznam podporovaných formátov.
XvImageFormatValues *formats = XvListImageFormats(getDpy(), m_port, &count); if (formats) { for (int i = 0; i < count; ++i) { qDebug() << "Format:" << formats[i].id; if (formats[i].type == XvRGB) { qDebug() << " type: RGB"; } else if (formats[i].type == XvYUV) { qDebug() << " type: YUV"; } if (formats[i].byte_order == LSBFirst) { qDebug() << " byte order: LSB"; } else if (formats[i].byte_order == MSBFirst) { qDebug() << " byte order: MSB"; } qDebug() << " bits per pixel:" << formats[i].bits_per_pixel; if (formats[i].format == XvPacked) { qDebug() << " format: Packed"; } else if (formats[i].format == XvPlanar) { qDebug() << " format: Planar"; } qDebug() << " num planes:" << formats[i].num_planes; if (formats[i].type == XvRGB) { qDebug() << " depth:" << formats[i].depth; qDebug() << " red mask:" << QString("%1").arg(formats[i].red_mask, 0, 16); qDebug() << " green mask:" << QString("%1").arg(formats[i].green_mask, 0, 16); qDebug() << " blue mask:" << QString("%1").arg(formats[i].blue_mask, 0, 16); } else if (formats[i].type == XvYUV) { qDebug() << " y sample bits:" << formats[i].y_sample_bits; qDebug() << " u sample bits:" << formats[i].u_sample_bits; qDebug() << " v sample bits:" << formats[i].v_sample_bits; qDebug() << " horz y period:" << formats[i].horz_y_period; qDebug() << " horz u period:" << formats[i].horz_u_period; qDebug() << " horz v period:" << formats[i].horz_v_period; qDebug() << " vert y period:" << formats[i].vert_y_period; qDebug() << " vert u period:" << formats[i].vert_u_period; qDebug() << " vert v period:" << formats[i].vert_v_period; qDebug() << " component order:" << formats[i].component_order; if (formats[i].scanline_order == XvTopToBottom) { qDebug() << " scanline order: TopToBottom"; } else if (formats[i].scanline_order == XvBottomToTop) { qDebug() << " scanline order: BottomToTop"; } } } XFree(formats); }
Ovládače Intel GMA podporujú nasledujúce formáty:
Format: 844715353 type: YUV byte order: LSB bits per pixel: 16 format: Packed num planes: 1 y sample bits: 8 u sample bits: 8 v sample bits: 8 horz y period: 1 horz u period: 2 horz v period: 2 vert y period: 1 vert u period: 1 vert v period: 1 component order: YUYV scanline order: TopToBottom Format: 842094169 type: YUV byte order: LSB bits per pixel: 12 format: Planar num planes: 3 y sample bits: 8 u sample bits: 8 v sample bits: 8 horz y period: 1 horz u period: 2 horz v period: 2 vert y period: 1 vert u period: 2 vert v period: 2 component order: YVU scanline order: TopToBottom Format: 808596553 type: YUV byte order: LSB bits per pixel: 12 format: Planar num planes: 3 y sample bits: 8 u sample bits: 8 v sample bits: 8 horz y period: 1 horz u period: 2 horz v period: 2 vert y period: 1 vert u period: 2 vert v period: 2 component order: YUV scanline order: TopToBottom Format: 1498831189 type: YUV byte order: LSB bits per pixel: 16 format: Packed num planes: 1 y sample bits: 8 u sample bits: 8 v sample bits: 8 horz y period: 1 horz u period: 2 horz v period: 2 vert y period: 1 vert u period: 1 vert v period: 1 component order: UYVY scanline order: TopToBottom
Pri vykresľovaní som vybral prvý formát zo zoznamu v minulej kapitole. Video buffer je umiestnený v inštancii triedy QVideoFrame
. To je v tomto prípade YUYV
. Budeme chcieť vykresliť obraz o veľkosti 400 x 300 pixelov. Vo výpise vidieť, že počet bitov na pixel je 16 tj. každý pixel je zakódovaný do 2 bytov. Nasledujúcim kódom alokujeme video buffer s read-write prístupom k bitom.
int w = 400; int h = 300; QSize s(400, 300); QVideoFrame videoFrame = QVideoFrame(w * h * 2, s, w * 2, QVideoFrame::Format_YUYV); videoFrame.map(QAbstractVideoBuffer::ReadWrite);
Prvým argumentom konštruktora QVideoFrame je veľkosť alokovaného bufferu (veľkosť obrázku vynásobená 2 pretože každý pixel je zakódovaný do práve 2 bytov). Nasleduje veľkosť obrázku, počet bytov na riadok a formát.
Následne môžme pracovať s bytmi, napr. vyplniť ich nudnou šedou farbou . Zo štruktúry YUYV je vidieť, že na každú zložku luminancie pripadá len jedná chromatická zložka. Zložky U a V sú teda spoločné pre jeden makroblok veľkosti 2 x 1 px. Preto pre každý druhý pixel nastavujeme hodnotu U a V. Pre prevod z RGB do YUV som použil jednoduchú aproximáciu.
unsigned char rgb2y(unsigned char r, unsigned char g, unsigned char b) { return ((r * 66 + g * 129 + b * 25) >> 8) + 16; } unsigned char rgb2u(unsigned char r, unsigned char g, unsigned char b) { return ((r * (-38) + g * (-74) + b * 112) >> 8) + 128; } unsigned char rgb2v(unsigned char r, unsigned char g, unsigned char b) { return ((r * 112 + g * (-94) + b * (-18)) >> 8) + 128; } ... for (size_t px = 0; px < (w * h); ++px) { videoFrame.bits()[px << 1] = rgb2y(127, 127, 127); if (px % 2 == 0) { videoFrame.bits()[(px << 1) + 1] = rgb2u(127, 127, 127); } else { videoFrame.bits()[(px << 1) + 1] = rgb2v(127, 127, 127); } }
Ostalo už len samotné vykreslenie obrazových dát. Na to budeme potrebovať vytvoriť XvImage
odkazujúci na dáta z QVideoFrame
, získať grafický kontext GC, vykresliť obrázok XvPutImage
a uvoľniť prostriedky.
.
Display *dpy = getDpy(); char *bits = reinterpret_cast<char *>(videoFrame.bits()); XvImage *image = XvCreateImage(dpy, port, format_id, bits, w, h); XGCValues xgcv; GC gc = XCreateGC(dpy, winId(), 0L, &xgcv); XvPutImage(dpy, port, winId(), gc, image, 0, 0, w, h, 0, 0, win->width(), win->height()); XFreeGC(dpy, gc); XFree(image);
Pri vytváraní inštancie XvImage musíme konvertovať uchar *
na char *
kvôli inému očakávanému typu XvCreateImage
. Port a id formátu sme získali z informácií o adaptéroch a formátoch.
Volanie XvPutImage
, má na prvý pohľad hrôzostrašný počet parametrov. V skutočnosti táto funkcia umožňuje orezanie obrazu a jeho škálovanie pri vykresľovaní. Preto posledné 4 parametre sú poloha a veľkosť vykresľovaného obrazu voči počiatočným súradniciam widgetu. Predposledné 4 parametre sú poloha a veľkosť vykresľovacieho okna pixmapy.
Pomocou atribútov sa dá nastaviť obyčajne jas, kontrast, saturácia alebo gamma. Zoznam atribútov sa dá získať volaním XvQueryPortAttributes
.
int count = 0; XvAttribute *attributes = XvQueryPortAttributes(getDpy(), m_port, &count); if (attributes) { for (int i = 0; i < count; ++i) { qDebug() << attributes[i].name; qDebug() << " min:" << attributes[i].min_value; qDebug() << " max:" << attributes[i].max_value; if (attributes[i].flags == ReadFlag) { qDebug() << " flags: Read"; } else if (attributes[i].flags == WriteFlag) { qDebug() << " flags: Write"; } else if (attributes[i].flags == (ReadFlag | WriteFlag)) { qDebug() << " flags: Read | Write"; } } XFree(attributes); }
Výstup na mojej grafickej karte vyzerá nasledovne:
XV_COLORKEY min: 0 max: 16777215 flags: Read | Write XV_BRIGHTNESS min: -128 max: 127 flags: Read | Write XV_CONTRAST min: 0 max: 255 flags: Read | Write XV_SATURATION min: 0 max: 1023 flags: Read | Write XV_PIPE min: -1 max: 1 flags: Read | Write XV_GAMMA0 min: 0 max: 16777215 flags: Read | Write XV_GAMMA1 min: 0 max: 16777215 flags: Read | Write XV_GAMMA2 min: 0 max: 16777215 flags: Read | Write XV_GAMMA3 min: 0 max: 16777215 flags: Read | Write XV_GAMMA4 min: 0 max: 16777215 flags: Read | Write XV_GAMMA5 min: 0 max: 16777215 flags: Read | Write
Hodnota atribútu sa dá získať nasledujúcim kódom:
Atom atom = XInternAtom(getDpy(), "XV_BRIGHTNESS", True); int value; XvGetPortAttribute(getDpy(), port, atom, &value);
Záhadná konštanta True hovorí funkcii, že má vrátiť chybu v prípade, že atribút neexistuje.
Nastavenie atribútu má veľmi podobný kód:
Atom atom = XInternAtom(getDpy(), "XV_BRIGHTNESS", True); XvSetPortAttribute(getDpy(), port, atom, 50);
Na tomto mieste som chcel ukázať niečo fakt praktické, ale nevyšlo Takže namiesto toho je tu môj testovací obrazec pre nastavenie atribútov Xv (pozor, funguje len s YUYV). Kód je dostupný na githube. K praktickému použitiu Xv sa hádam dostanem v budúcom blogu
Tiskni Sdílej:
+1