Prima parte:
Ce? Un codec video este o componentă software/hardware care comprimă și/sau decomprimă video digital.
De ce? În ciuda anumitor limitări în ceea ce privește lățimea de bandă și
spațiul de stocare, piața cere video de o calitate tot mai bună. Îți amintești cum în postarea anterioară am calculat minimul necesar pentru 30 de cadre pe secundă, 24 de biți pe pixel, cu o rezoluție de 480×240? Am obținut 82,944 Mbit/s fără compresie. Compresia este, deocamdată, singura modalitate de a transmite HD/FullHD/4K pe ecranele TV și pe Internet. Cum se realizează asta? Acum vom examina pe scurt metodele principale.
Traducerea a fost realizată cu sprijinul companiei EDISON Software.Ne ocupăm de , dar și pentru .
Codec vs Container
O greșeală comună a începătorilor este să confunde codec-ul video digital cu containerul video digital. Un container este un format, o învelire care conține metadate video (și, poate, audio). Video comprimat poate fi văzut ca o încărcătură a containerului.
De obicei, extensia fișierului video indică tipul său de container. De exemplu, fișierul video.mp4 este, probabil, un container MPEG-4 Part 14, iar un fișier cu numele video.mkv este, cel mai probabil, . Pentru a fi complet sigur de codec și formatul containerului, poți folosi sau .
Puțin istorie
Înainte de a trece la Cum?, haideți să ne scufundăm puțin în istorie pentru a înțelege mai bine unele codecuri mai vechi.
Codec video H.261 a apărut în 1990 (tehnic, în 1988) și a fost creat pentru a funcționa la o viteză de transmisie de 64 Kbit/s. Acesta utiliza deja idei precum subextragerea culorilor, macroblocurile etc. În 1995 a fost publicat standardul codec-ului video H.263, care a evoluat până în 2001.
În 2003 a fost finalizată prima versiune H.264/AVC. În același an, compania „TrueMotion” a lansat un codec video gratuit, care comprima video cu pierderi numit VP3. În 2008, Google a cumpărat această companie, lansând VP8 în același an. În decembrie 2012, Google a lansat VP9, și este suportat de aproximativ ¾ din piața browserelor (inclusiv dispozitivele mobile).
AV1 — este un nou codec video gratuit, cu sursă deschisă, dezvoltat Alianța pentru Media Deschise (AOMedia), care include companii de renume, precum: Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel și Cisco. Prima versiune a codec-ului 0.1.0 a fost publicată pe 7 aprilie 2016.
Nașterea AV1
La începutul anului 2015, Google lucra la VP10, Xiph (care aparține Mozilla) lucra la Daala, iar Cisco a creat un codec video gratuit numit Thor.
Apoi MPEG LA a anunțat inițial limite anuale pentru HEVC (H.265) și o taxă, de 8 ori mai mare decât pentru H.264, dar în curând au modificat din nou regulile:
fără limită anuală,
taxa pe conținut (0,5% din venituri) și
taxa per unitate de produs este de aproximativ 10 ori mai mare decât pentru H.264.
Alianța pentru Media Deschise a fost creată de companii din diverse domenii: producători de hardware (Intel, AMD, ARM, Nvidia, Cisco), furnizori de conținut (Google, Netflix, Amazon), creatori de browsere (Google, Mozilla) și altele.
Companiile aveau un obiectiv comun — un codec video fără taxe de licențiere. Apoi apare AV1 cu o licență patentată mult mai simplă. Timothy B. Terriberry a făcut o prezentare uimitoare, care a devenit sursa conceptului actual al AV1 și a modelului său de licență.
Veți fi surprinși să aflați că puteți analiza codec-ul AV1 prin intermediul browserului (cei interesați pot accesa).

Codec universal
Să analizăm mecanismele de bază, care stau la baza codec-ului video universal. Cele mai multe dintre aceste concepte sunt utile și sunt folosite în codec-urile moderne, precum VP9, AV1 și HEVC. Vă avertizez că multe dintre lucrurile explicate vor fi simplificate. Uneori vor fi folosite exemple reale (ca în cazul H.264) pentru a demonstra tehnologiile.
Primul pas — împărțirea imaginii
Primul pas este divizarea cadrelor în mai multe secțiuni, subsecțiuni și așa mai departe.

De ce? Există numeroase motive. Atunci când împărțim imaginea, putem prognoza mai precis vectorul de mișcare, folosind secțiuni mici pentru părțile mobile. În timp ce pentru fundalul static putem folosi și secțiuni mai mari.
De obicei, codec-urile organizează aceste secțiuni în secțiuni (sau fragmente), macroblocuri (sau blocuri de arbori de codare) și numeroase subsecțiuni. Dimensiunea maximă a acestor secțiuni variază, HEVC stabilește 64×64, în timp ce AVC folosește 16×16, iar subsecțiunile pot fi împărțite până la dimensiuni de 4×4.
Îți amintești diferitele tipuri de cadre din articolul anterior?! Acestea pot fi aplicate și blocurilor, așa că putem avea un fragment I, un bloc B, un macrobloc P etc.
Pentru cei care doresc să exerseze — priviți cum o imagine se împarte în secțiuni și subsecțiuni. Pentru aceasta, se poate folosi ceea ce am menționat deja în articolul anterior. (cel care este plătit, dar cu o versiune de încercare gratuită, având o limitare la primele 10 cadre). Aici sunt analizate secțiunile. VP9:

Pasul 2 — previziune
De îndată ce avem secțiuni, putem formula previziuni astrologice pe baza lor. Pentru INTER-previzionare este necesar să transmitem vectorii de mișcare și restul, iar pentru INTRA-previzionare se transmite direcția previziunii și restul.
Pasul 3 — transformare
După ce obținem blocul rezidual (secțiune prezisă → secțiune reală), poate fi transformat astfel încât să știm ce pixeli pot fi eliminați, păstrând în același timp calitatea generală. Există anumite transformări care asigură un comportament precis.
Deși există și alte metode, să examinăm mai atent transformarea discretă a cosinusului (DCT — de la transformarea cosinusului discret). Funcțiile principale ale DCT:
- Transformă blocurile de pixeli în blocuri de coeficienți de frecvență de dimensiuni egale.
- Concentrează puterea, ajutând la eliminarea redundanței spațiale.
- Asigură reversibilitatea.
Pe 2 februarie 2017, Cintra R.J. și Bayer F.M. au publicat un articol despre transformarea similară DCT pentru compresia imaginilor, care necesită doar 14 adunări.
Nu te îngrijora dacă nu ai înțeles beneficiile fiecărei puncte. Acum, cu exemple concrete, ne vom convinge de valoarea lor reală.
Să luăm un astfel de bloc de pixeli 8×8:

Acest bloc este redat în imaginea următoare de 8 pe 8 pixeli:

Aplicăm DCT la acest bloc de pixeli și obținem un bloc de coeficienți de dimensiune 8×8:

Și dacă redăm acest bloc de coeficienți, obținem această imagine:

După cum vedem, nu arată ca imaginea originală. Putem observa că primul coeficient este foarte diferit de toate celelalte. Acest prim coeficient este cunoscut sub numele de coeficient DC, care reprezintă toate eșantioanele din matricea de intrare, asemănându-se cu o medie.
Această blocă de coeficienți are o proprietate interesantă: separă componentele de frecvență înaltă de cele de frecvență joasă.

În imagine, cea mai mare parte a puterii este concentrată pe frecvențe mai joase, așa că, dacă transformăm imaginea în componentele sale de frecvență și eliminăm coeficienții de frecvență mai înaltă, putem reduce cantitatea de date necesare pentru a descrie imaginea fără a compromite prea mult calitatea acesteia.
Frecvența înseamnă cât de repede se schimbă semnalul.
Să încercăm să aplicăm cunoștințele dobândite în exemplul de testare, transformând imaginea originală în frecvența sa (bloca de coeficienți), folosind DCT, și apoi eliminând o parte din coeficienții cei mai puțin importanți.
Mai întâi, o convertim în domeniul frecvenței.

Apoi, eliminăm o parte (67%) din coeficienți, în principal partea din colțul din dreapta jos.

În cele din urmă, restaurăm imaginea din această blocă de coeficienți eliminată (amintiți-vă, trebuie să fie reversibil) și o comparăm cu originalul.

Observăm că se aseamănă cu imaginea originală, dar există multe diferențe față de original. Am eliminat 67,1875% și totuși am obținut ceva care seamănă cu sursa originală. S-ar fi putut elimina coeficienții mai judicios pentru a obține o imagine de o calitate și mai bună, dar aceasta este deja o temă viitoare.
Fiecare coeficient este format folosind toți pixele.
Este important: fiecare coeficient nu este direct asociat cu un singur pixel, ci reprezintă o sumă ponderată a tuturor pixelilor. Acest grafic uimitor arată cum este calculat primul și al doilea coeficient folosind greutăți unice pentru fiecare index.
De asemenea, puteți încerca să vizualizați DCT, aruncând o privire asupra formării simple a imaginii pe baza acestuia. De exemplu, iată simbolul A, format folosind fiecare greutate a coeficientului:
Pasul 4 - cuantificare
După ce eliminăm anumiți coeficienți în pasul anterior, în ultimul pas (transformare), aplicăm o formă specială de cuantificare. În această etapă, este permisă pierderea de informație. Sau, mai simplu spus, vom cuantifica coeficienții pentru a obține compresie.
Cum putem cuantifica un bloc de coeficienți? Una dintre cele mai simple metode este cuantificarea uniformă, când luăm blocul, îl împărțim la o valoare (la 10) și rotunjim rezultatul obținut.

Putem inversa acest bloc de coeficienți? Da, putem, înmulțind cu aceeași valoare cu care am împărțit.

Această metodă nu este cea mai bună, deoarece nu ia în considerare importanța fiecărui coeficient. Am putea folosi o matrice de cuantificatori în loc de o singură valoare, iar această matrice poate aplica proprietatea DCT, cuantificând majoritatea coeficientelor din colțul din dreapta jos și mai puțini din colțul din stânga sus.
Pasul 5 — codificare prin entropie
După ce am cuantificat datele (blocuri de imagini, fragmente, cadre), putem continua să le comprimăm fără pierderi. Există multe metode algoritmice de compresie a datelor. Ne vom familiariza pe scurt cu unele dintre ele, pentru o înțelegere mai profundă puteți citi cartea „Înțelegerea compresiei: Compresia datelor pentru dezvoltatorii moderni” ("»).
Codificarea video cu VLC
Să presupunem că avem un flux de caractere: a, e, r și t. Probabilitatea (între 0 și 1) cu care apare fiecare caracter în flux este reprezentată în acest tabel.
| a | e | r | t | |
|---|---|---|---|---|
| Probabilitate | 0,3 | 0,3 | 0,2 | 0,2 |
Putem aloca coduri binare unice (preferabil mici) celor mai probabile, iar coduri mai mari celor mai puțin probabile.
| a | e | r | t | |
|---|---|---|---|---|
| Probabilitate | 0,3 | 0,3 | 0,2 | 0,2 |
| Cod binar | 0 | 10 | 110 | 1110 |
Compresăm fluxul, presupunând că în final vom folosi 8 biți pentru fiecare caracter. Fără compresie, ar fi necesari 24 de biți pentru fiecare caracter. Dacă înlocuim fiecare caracter cu codul său, obținem o economisire!
Primul pas este codificarea caracterului e, care este 10, iar al doilea caracter este a, care se adaugă (nu printr-o metodă matematică): [10] [0], și, în final, al treilea caracter t, ceea ce face ca fluxul nostru final de biți comprimat să fie [10] [0] [1110] sau 1001110, pentru care sunt necesari doar 7 biți (cu 3,4 ori mai puțin spațiu decât în original).
Rețineți că fiecare cod trebuie să fie un cod unic cu prefix. va ajuta la găsirea acestor cifre. Deși această metodă nu este fără defecte, există codecuri video care oferă în continuare această metodă algoritmică de compresie.
Atât codificatorul, cât și decodificatorul trebuie să aibă acces la tabela caracterelor cu codurile lor binare. De aceea, este necesar să se trimită și tabela în datele de intrare.
Codificare aritmetică
Să presupunem că avem un flux de caractere: a, e, r, s și t, iar probabilitatea lor este reprezentată de această tabelă.
| a | e | r | s | t | |
|---|---|---|---|---|---|
| Probabilitate | 0,3 | 0,3 | 0,15 | 0,05 | 0,2 |
Cu această tabelă vom construi intervale care conțin toate caracterele posibile, sortate după frecvența lor.

Acum să codificăm un flux format din trei caractere: eat.
Mai întâi alegem primul caracter e, care se află în subintervalul de la 0,3 la 0,6 (exclusiv). Luăm acest subinterval și îl împărțim din nou în aceleași proporții ca înainte, dar pentru acest nou interval.

Să continuăm să codificăm fluxul nostru eat. Acum luăm al doilea caracter a, care se află în noul subinterval de la 0,3 la 0,39, iar apoi luăm ultimul nostru caracter t și, repetând aceeași procedură, obținem ultimul subinterval de la 0,354 la 0,372.

Trebuie doar să alegem un număr în ultimul subinterval de la 0,354 la 0,372. Să alegem 0,36 (dar putem alege orice alt număr în acest subinterval). Numai cu acest număr vom putea recupera fluxul nostru original. Este ca și cum am trasa o linie în interiorul intervalelor pentru a codifica fluxul nostru.

Operația inversă (adică decodificare) este la fel de simplă: cu numărul nostru 0,36 și intervalul nostru inițial, putem începe același proces. Dar acum, folosind acest număr, vom descoperi fluxul codificat cu acest număr.
În primul interval observăm că numărul nostru corespunde unei secțiuni, astfel că acesta este primul nostru caracter. Acum împărțim din nou acest subinterval, efectuând aceeași procedură ca înainte. Aici putem observa că 0,36 corespunde caracterului a, și după repetarea procesului am ajuns la ultimul caracter t (formând fluxul nostru codificat original eat).
Atât pentru codificator, cât și pentru decodificator, trebuie să existe o tabelă de probabilități a caracterelor, astfel încât aceasta să fie inclusă în datele de intrare.
Destul de elegant, nu-i așa? Cineva care a inventat această soluție a fost extrem de inteligent. Unele codecuri video folosesc această tehnică (sau, în orice caz, o oferă ca opțiune).
Ideea este de a comprima fără pierderi un flux de biți cuantificat. Cu siguranță, acest articol nu oferă toate detaliile, motivele, compromisurile etc. Dar, dacă ești dezvoltator, ar trebui să știi mai multe. Codec-urile noi încearcă să folosească diferite algoritmi de codare a entropiei, cum ar fi ANS.
Pasul 6 - formatul fluxului de biți
După ce ai realizat toate acestea, trebuie să desfaci cadrele comprimate în contextul pașilor executați. Este necesar să informați explicit decodorul despre deciziile luate de codificator. Decodorului trebuie să i se ofere toate informațiile necesare: adâncime de biți, spațiu de culoare, rezoluție, informații despre predicții (vectori de mișcare, predicție INTER direcționată), profil, nivel, frecvență de cadre, tip de cadru, număr de cadru și multe altele.
Vom face o introducere superficială în fluxul de biți H.264. Primul nostru pas este să creăm un flux de biți H.264 minim (FFmpeg adaugă în mod implicit toate parameterele de codare, cum ar fi SEI NAL — vom descoperi mai în detaliu ce este). Putem face acest lucru folosind propriul nostru repository și FFmpeg.
. /s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
Această comandă va genera un flux de biți brut H.264 cu un cadru, o rezoluție de 64×64, cu un spațiu de culoare YUV420. Utilizăm următoarea imagine ca și cadru.

Fluxul de biți H.264
Standard AVC (H.264) definește că informația va fi trimisă în macrocadre (în înțelesul rețelei), numite NAL (acesta este un astfel de nivel de abstractizare a rețelei). Scopul principal al NAL este de a oferi o reprezentare „prietenoasă pentru rețea” a video-ului. Acest standard ar trebui să funcționeze pe televizoare (pe baza fluxurilor), în internet (bazat pe pachete).
![]()
Există un marker de sincronizare pentru a determina limitele elementelor NAL. Fiecare marker de sincronizare conține o valoare 0x00 0x00 0x01, cu excepția primului, care este egal cu 0x00 0x00 0x00 0x01. Dacă rulăm hexdump pentru fluxul de biți H.264 generat, vom identifica cel puțin trei modele NAL la începutul fișierului.

Așa cum am spus, decodorul trebuie să știe nu doar datele imaginii, ci și detaliile video, cadru, culoare, parametrii utilizați și multe altele. Primul octet din fiecare NAL definește categoria și tipul său.
| Identificatorul tipului NAL | Descriere |
|---|---|
| 0 | Tip necunoscut |
| 1 | Fragmentul de imagine codificat fără IDR |
| 2 | Secțiunea de date codificată a segmentului A |
| 3 | Secțiunea de date codificată a segmentului B |
| 4 | Secțiunea de date codificată a segmentului C |
| 5 | Fragmentul IDR codificat al imaginii IDR |
| 6 | Informații suplimentare despre extensia SEI |
| 7 | Setul de parametri al secvenței SPS |
| 8 | Setul de parametri al imaginii PPS |
| 9 | Separator de acces |
| 10 | Sfârșitul secvenței |
| 11 | Sfârșitul fluxului |
| … | … |
De obicei, primul NAL din fluxul video este SPS. Acest tip de NAL este responsabil pentru informarea despre variabilele comune de codare, cum ar fi profilul, nivelul, rezoluția și altele.
Dacă sărim peste primul marker de sincronizare, putem decodifica primul octet pentru a afla ce tip de NAL este primul.
De exemplu, primul octet după markerul de sincronizare este 01100111, unde prima biți (0) se află în câmpul forbidden_zero_bit. Următorii 2 biți (11) ne anunță câmpul nal_ref_idc, care indică dacă acest NAL este un câmp de referință sau nu. Și restul de 5 biți (00111) ne anunță câmpul nal_unit_type, în acest caz, este un bloc SPS (7) NAL.
Al doilea octet (binary=01100100, hex=0x64, dec=100) din SPS NAL este câmpul profile_idc, care arată profilul utilizat de codificator. În acest caz, a fost utilizat profilul înalt restricționat (adică profilul înalt fără suport pentru segmentele B bidirecționale).

Dacă consultăm specificația fluxului de biți H.264 pentru SPS NAL, vom descoperi multe valori pentru denumirea parametrului, categorie și descriere. De exemplu, să ne uităm la câmpurile pic_width_in_mbs_minus_1 și pic_height_in_map_units_minus_1.
| Denumirea parametrului | Categorie | Descriere |
|---|---|---|
| pic_width_in_mbs_minus_1 | 0 | ue(v) |
| pic_height_in_map_units_minus_1 | 0 | ue(v) |
Dacă facem unele operații matematice cu valorile acestor câmpuri, vom obține rezoluția. Poate fi reprezentat 1920 x 1080, folosind pic_width_in_mbs_minus_1 cu valoarea 119 ((119 + 1) * macroblock_size = 120 * 16 = 1920). Din nou, economisind spațiu, în loc să codificăm 1920, am făcut acest lucru cu 119.
Dacă continuăm să verificăm video creat în format binar (de exemplu: xxd -b -c 11 v/minimal_yuv420.h264), putem trece la ultimul NAL, care este cadrul în sine.

Aici vedem primele 6 valori ale octeților: 01100101 10001000 10000100 00000000 00100001 11111111. Deoarece știm că primul octet indică tipul de NAL, în acest caz (00101) este un fragment IDR (5), și atunci putem explora suplimentar:

Folosind informațiile din specificație, putem decodifica tipul fragmentului (slice_type) și numărul cadrului (frame_num) printre alte câmpuri importante.
Pentru a obține valorile unor câmpuri (ue(v), me(v), se(v) sau te(v), trebuie să decodificăm fragmentul folosind un decoder specializat, bazat pe . Această metodă este foarte eficientă pentru codificarea valorilor variabile, mai ales atunci când există multe valori implicite.
Valorile slice_type și frame_num acestui video sunt egale cu 7 (fragment I) și 0 (cadru inițial).
Un flux de biți poate fi văzut ca un protocol. Dacă doriți să aflați mai multe despre fluxul de biți, ar trebui să consultați specificația ITU H.264. Iată un diagramă care arată unde sunt datele imaginii (YUV în format comprimat).

Puteți explora și alte fluxuri de biți, cum ar fi VP9, H.265 (HEVC) sau chiar noul nostru cel mai bun flux de biți AV1. Oare toate sunt similare? Nu, dar înțelegând măcar unul - este mult mai ușor să înțelegem pe celelalte.
Vreți să exersați? Explorați fluxul de biți H.264
Puteți genera un video cu un cadru și utiliza MediaInfo pentru a explora fluxul de biți H.264. De fapt, nimic nu vă împiedică să priviți chiar și codul sursă care analizează fluxul de biți. H.264 (AVC).
Pentru practică, puteți folosi Intel Video Pro Analyzer (se pare că am menționat deja că programul este plătit, dar există o versiune demo gratuită, cu o limită de 10 cadre?).
Prezentare generală
Merită menționat că multe codecuri moderne folosesc aceeași model pe care tocmai l-am studiat. Hai să aruncăm o privire la diagrama codec-ului video Thor. Aceasta conține toate etapele pe care le-am parcurs. Tot sensul acestei note este să înțelegeți, măcar puțin, inovațiile și documentația din acest domeniu.

Anterior am calculat că ar fi nevoie de 139 GB de spațiu pe disc pentru a stoca un fișier video de o oră la calitate 720p și 30 fps. Dacă folosim metodele analizate în acest articol (predicții inter-cadru și intra-cadru, transformare, cuantizare, codificare entropică etc.), putem obține (pe baza cheltuirii a 0,031 biți pe pixel) un video de calitate acceptabilă, ocupând doar 367,82 MB, nu 139 GB.
Cum H.265 atinge un grad mai bun de compresie decât H.264?
Acum, când se știe mai multe despre cum funcționează codec-urile, este mai ușor să înțelegem cum codec-urile noi pot oferi o rezoluție mai mare cu mai puțini biți.
Când compari AVC și HEVC, să nu uităm că de cele mai multe ori este o alegere între o încărcare mai mare pe CPU și gradul de compresie.
HEVC are mai multe opțiuni de secțiuni (și subsecțiuni) decât AVC, mai multe direcții de prognoză internă, codificare entropică îmbunătățită și multe altele. Toate aceste îmbunătățiri au făcut H.265 capabil să comprime cu 50% mai mult decât H.264.

Prima parte:
Sursa: habr.com




