Preskúmavame VoIP engine Mediastreamer2. Časť 11

Materiál článku je prevzatý z môjho zen kanála.

Preskúmavame VoIP engine Mediastreamer2. Časť 11

Mechanizmovanie dát

  • Blok dát dblk_t
  • Správa mblk_t
  • Funkcie správy správ mblk_t
  • Fronta queue_t
  • Funkcie správy front queue_t
  • Spojenie filtrov
  • Signálny bod grafu spracovania dát
  • Zákulisná činnosť tickeru
  • Buffer (MSBufferizer)
  • Funkcie správy MSBufferizer

V minulosti článku Vyvinuli sme vlastný filter. Tento článok venujeme zariadeniu vnútorného mechanizmu presunu dát medzi filtrami mediastreamera. To umožní v budúcnosti písať sofistikované filtre s menšími námahou.

Mechanizmovanie dát

Presun dát v mediastreameri sa vykonáva pomocou front, ktoré sú popísané štruktúrou queue_t. Po frontách sa presúvajú reťazce správ typu mblk_t, ktoré samy o sebe neobsahujú dáta signálu, ale iba odkazy na predchádzajúcu, nasledujúcu správu a na blok dát. Okrem toho chcem osobitne zdôrazniť, že existuje aj pole na odkaz na správu rovnakého typu, ktoré umožňuje usporiadať jednosmerný zoznam správ. Skupinu správ, spojených takýmto zoznamom, budeme nazývať tuple. Týmto spôsobom sa akýkoľvek prvok fronty môže ocitnúť ako jediná správa mblk_t, ale môže byť aj hlavou tuple správ mblk_t. Každá správa tuple môže mať svoj podriadený blok dát. Prečo sú tuple potrebné, prediskutujeme o niečo neskôr.

Ako bolo uvedené vyššie, sama správa neobsahuje blok dát, namiesto toho obsahuje iba ukazovateľ na oblasť pamäte, kde je blok uložený. V tejto časti vyzerá celkový obraz práce mediastreamera ako sklad dverí v animovanom filme "Monsters, Inc.", kde dvere (odkazy na dáta — miestnosti) sa šialenou rýchlosťou pohybujú po visiacich dopravníkoch, pričom samotné miestnosti zostávajú nepohnuté.

Teraz sa, idúc hierarchiou zdola nahor, podrobne pozrime na uvedené entity mechanizmu prenosu dát v mediastreameri.

Blok dát dblk_t

Blok dát pozostáva z hlavičky a dátového buffera. Hlavička je popísaná nasledujúcou štruktúrou,

typedef struct datab
{
unsigned char *db_base; // Ukazovateľ na začiatok dátového bufferu.
unsigned char *db_lim;  // Ukazovateľ na koniec dátového bufferu.
void (*db_freefn)(void*); // Funkcia na uvoľnenie pamäte pri odstránení bloku.
int db_ref; // Počítadlo odkazov.
} dblk_t;

Polia štruktúry obsahujú ukazovatele na začiatok vyrovnávacej pamäte, koniec vyrovnávacej pamäte a funkciu na odstránenie dátovej vyrovnávacej pamäte. Posledný prvok v hlavičke db_ref — čítač odkazov, ak dosiahne nulu, slúži ako signál na odstránenie tohto bloku z pamäte. Ak bol blok dát vytvorený funkciou datab_alloc() , tak vyrovnávacia pamäť bude umiestnená v pamäti hneď za hlavičkou. Vo všetkých ostatných prípadoch môže byť vyrovnávacia pamäť umiestnená niekde inde. V dátovej vyrovnávacej pamäti budú uložené odpočty signálu alebo iné údaje, ktoré chceme spracovávať filtrami.

Nový exemplár bloku dát sa vytvára pomocou funkcie:

dblk_t *datab_alloc(int size);

Ako vstupný parameter sa odovzdáva veľkosť dát, ktoré bude blok uchovávať. Pamäť sa vyčlení väčšia, aby sa na začiatku vyčlenenej pamäte umiestnila hlavička — štruktúra datab. Avšak pri použití iných funkcií to tak vždy neplatí, v niektorých prípadoch môže byť vyrovnávacia pamäť umiestnená oddelene od hlavičky bloku dát. Polia štruktúry sa pri vytvorení nastavujú tak, aby pole db_base ukazovalo na začiatok oblasti dát a db_lim na jej koniec. Čítač odkazov db_ref sa nastaví na jednu. Ukazovateľ funkcie na vyčistenie dát sa nastaví na nulu.

Správa mblk_t

Ako už bolo spomenuté, prvky frontu majú typ mblk_t, je definovaný nasledovne:

typedef struct msgb
{
  struct msgb *b_prev;   // Ukazovateľ na predchádzajúci prvok zoznamu.
  struct msgb *b_next;   // Ukazovateľ na nasledujúci prvok zoznamu.
  struct msgb *b_cont;   // Ukazovateľ na pripájanie iných správ k správe, na vytvorenie zloženia správ.
  struct datab *b_datap; // Ukazovateľ na štruktúru bloku dát.
  unsigned char *b_rptr; // Ukazovateľ na začiatok oblasti dát na čítanie údajov z vyrovnávacej pamäte b_datap.
  unsigned char *b_wptr; // Ukazovateľ na začiatok oblasti dát na zápis údajov do vyrovnávacej pamäte b_datap.
  uint32_t reserved1;    // Rezervované pole1, mediastreamer tam umiestňuje pomocné informácie.
  uint32_t reserved2;    // Rezervované pole2, mediastreamer tam umiestňuje pomocné informácie.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktura mblk_t na začiatku obsahuje ukazovatele b_prev, b_next, ktoré sú potrebné na organizáciu dvojitého zoznamu (ktorým je front queue_t).

Potom nasleduje ukazovateľ b_cont, ktorý sa používa iba vtedy, keď správa vstupuje do kótu. Pre poslednú správu v kóte zostáva tento ukazovateľ nulový.

Ďalej vidíme ukazovateľ na blok údajov b_datap, kvôli ktorému správa existuje. Za ním nasledujú ukazovatele na oblasť v rámci vyrovnávacej pamäte bloku. Pole b_rptr ukazuje miesto, z ktorého sa budú údaje čítať z vyrovnávacej pamäte. Pole b_wptr ukazuje miesto, z ktorého sa bude zapisovať do vyrovnávacej pamäte.

Zvyšné polia majú administratívny charakter a netýkajú sa fungovania mechanizmu prenosu údajov.

Nižšie je zobrazená jednotlivá správa s názvom m1 a blokom údajov d1.
Preskúmavame VoIP engine Mediastreamer2. Časť 11
Na nasledujúcom obrázku je zobrazený kót zo troch správ m1, m1_1, m1_2.
Preskúmavame VoIP engine Mediastreamer2. Časť 11

Funkcie na prácu so správami mblk_t

Nová správa mblk_t sa vytvára funkciou:

mblk_t *allocb(int size, int pri); 

založí novú správu v pamäti mblk_t s blokom údajov požadovanej veľkosti size, druhý argument — pri sa v skúmanej verzii knižnice nepoužíva. Mal by zostať nulový. Počas fungovania funkcie bude pridelená pamäť pre štruktúru novej správy a bude zavolaná funkcia mblk_init(), ktorá vymaže všetky polia vytvorenej štruktúry a potom, pomocou uvedenej vyššie, datab_alloc()vytvorí vyrovnávaciu pamäť. Potom sa vykoná konfigurácia polí v štruktúre:

mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;

Na výstupe získame novú správu s inicializovanými poliami a prázdnou vyrovnávacou pamäťou. Aby sa do správy pridali údaje, je potrebné vykonať ich kopírovanie do vyrovnávacej pamäte bloku údajov:

memcpy(msg->b_rptr, data, size);

kde data — ukazovateľ na zdroj údajov, a size — ich veľkosť.
následne je potrebné aktualizovať ukazovateľ na miesto zápisu, aby opäť ukazoval na začiatok voľnej oblasti v vyrovnávacej pamäti:

msg->b_wptr = msg->b_wptr + size

Ak je potrebné vytvoriť správu z už existujúcej vyrovnávacej pamäte bez kopírovania, tak sa na to používa funkcia:

mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); 

Funkcia po vytvorení správy a štruktúry bloku údajov nastaví jeho ukazovatele na údaje na adresu buf. T.j. v tomto prípade sa vyrovnávacia pamäť údajov nenachádza za poľami hlavičky bloku údajov, ako to bolo pri vytvorení bloku údajov funkciou datab_alloc()Predaná funkcii vyrovnávacia pamäť s údajmi zostane tam, kde bola, ale pomocou ukazovateľov bude posunutá na práve vytvorený hlavičku bloku údajov, a tá bude smerovať na správu.

K jednej správe mblk_t môžu byť postupne pripojené viaceré bloky údajov. To sa uskutočňuje funkciou:

mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); 

mp — správa, ku ktorej sa pridá ďalší blok údajov;
data — ukazovateľ na blok, ktorého kópia bude pridaná do správy;
size — veľkosť údajov;
pad — vlajka, že veľkosť prideleného priestoru musí byť vyrovnaná na hranicu 4 bajtov (doplnok bude vykonaný nulami).

Ak v existujúcej vyrovnávacej pamäti správy je dostatok miesta, nové údaje budú pripojené za už existujúce údaje. Ak sa zistí, že vo vyrovnávacej pamäti správy je menej ako size, vytvorí sa nová správa, s dostatočnou veľkosťou vyrovnávacej pamäti a údaje sa skopírujú do jej vyrovnávacej pamäte. Táto nová správa sa pripojí k pôvodnej pomocou ukazovateľa b_cont. V tomto prípade sa správa premení na kôpku.

Ak do kôpky treba pridať ďalší blok údajov, treba použiť funkciu:

void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);

tá nájde poslednú správu v kôpke (má b_cont nula) a pre túto správu vyvolá funkciu appendb().

Zistiť veľkosť údajov v správe alebo kôpke môžeme pomocou funkcie:

int msgdsize(const mblk_t *mp);

tá prejde všetkými správami kôpky a vráti celkové množstvo údajov v vyrovnávacích pamätiach týchto správ. Pre každú správu sa množstvo údajov vypočítava takto:

 mp->b_wptr - mp->b_rptr

Na zlúčenie dvoch kôpok sa používa funkcia:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

tá pripojí kôpku newm na koniec kôpky mp a vráti ukazovateľ na poslednú správu výslednej kôpky.

Ak je to potrebné, kôpku možno premeniť na jednu správu s jedným blokom údajov, to sa robí funkciou:

void msgpullup(mblk_t *mp,int len);

ak je argument len rovný -1, veľkosť prideleného vyrovnávacieho priestoru sa určuje automaticky. Ak len Ak je to kladné číslo, vytvorí sa vyrovnávacia pamäť tejto veľkosti a do nej sa skopírujú údaje správ kóty. Ak sa vyrovnávacia pamäť naplní, kopírovanie sa ukončí. Prvá správa kóty dostane vyrovnávaciu pamäť novej veľkosti so skopírovanými údajmi. Ostatné správy budú odstránené a pamäť vrátená do haldy.

Pri odstraňovaní štruktúry mblk_t sa zohľadňuje čítač odkazov bloku údajov, ak pri vyvolaní freeb() je rovný nule, blok údajov sa odstráni spolu s inštanciou mblk_t, na ktorú odkazuje.

Inicializácia polí novej správy:

void mblk_init(mblk_t *mp);

Pridanie ďalšej dávky údajov k správe:

mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);

Ak sa nové údaje nezmestia do voľného miesta vyrovnávacej pamäte správy, k správe sa pripojí samostatne vytvorená správa s potrebnou veľkosťou vyrovnávacej pamäte (v prvej správe sa nastaví ukazovateľ na pridanú správu) správa sa premení na kótu.

Pridanie dávky údajov do kóty:

void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); 

Funkcia volá appendb() v cykle.

Spojenie dvoch kót do jednej:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

Správa newm bude pripojené k mp.

Vytvorenie kópie jednotlivé správy:

mblk_t *copyb(const mblk_t *mp);

Úplné skopírovanie kóty so všetkými blokmi údajov:

mblk_t *copymsg(const mblk_t *mp);

Prvky kóty sa kopírujú funkciou copyb().

Vytvorenie ľahkej kópie správy. mblk_tPri tom sa blok údajov nekopíruje, ale zvyšuje sa čítač jeho odkazov. db_ref:

mblk_t *dupb(mblk_t *mp);

Vytvorenie ľahkej kópie kóty. Bloky údajov sa nekopírujú, iba sa zvyšujú ich čítače odkazov. db_ref:

mblk_t *dupmsg(mblk_t* m);

Zlepenie všetkých správ kóty do jednej správy:

void msgpullup(mblk_t *mp,size_t len);

Ak je argument len rovný -1, veľkosť rezervačnej vyrovnávacej pamäte sa určuje automaticky.

Odstránenie správy, kóty:

void freemsg(mblk_t *mp);

Čítač odkazov bloku údajov sa zníži o jeden. Ak pritom dosiahne nulu, blok údajov sa tiež odstráni.

Sčítanie celkového objemu údajov v správe alebo kóte.

size_t msgdsize(const mblk_t *mp);

Vyberanie správy z chvosta fronty:

mblk_t *ms_queue_peek_last (q);

Kopírovanie obsahu rezervovaných polí jednej správy do druhej správy (v skutočnosti tieto polia obsahujú vlajky, ktoré sa používajú mediastreamerom):

mblk_meta_copy(const mblk_t *source, mblk *dest);

Fronta queue_t

Riadková fronta v mediastreamere je realizovaná ako kruhový obojsmerný zoznam. Každý prvok zoznamu obsahuje ukazovateľ na blok dát s odpočtami signálu. To znamená, že sa posúvajú len ukazovatele na blok dát, zatiaľ čo samotné dáta zostávajú nepohybované. T.j. sa posúvajú len odkazy na ne.
Štruktúra popisujúca frontu queue_t, je zobrazená nižšie:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Prázdny" prvok fronty, neukazuje na dáta, používa sa len na riadenie fronty. Pri inicializácii fronty (qinit()) sa jeho ukazovatele nastavujú tak, aby ukazovali na neho samého. */
   int q_mcount;        // Počet prvkov vo fronte.
} queue_t;

Štruktúra obsahuje pole — ukazovateľ _q_stopper typu *mblk_t, ktorý ukazuje na prvý prvok (správu) vo fronte. Druhé pole štruktúry je čítač správ nachádzajúcich sa vo fronte.
Na obrázku nižšie je zobrazená fronta s názvom q1, obsahujúca 4 správy m1,m2, m3, m4.
Preskúmavame VoIP engine Mediastreamer2. Časť 11
Na nasledujúcom obrázku je zobrazená fronta s názvom q1, obsahujúca 4 správy m1,m2, m3, m4. Správa m2 je hlavou n-tice, do ktorej vedú ďalšie dve správy m2_1 a m2_2.

Preskúmavame VoIP engine Mediastreamer2. Časť 11

Funkcie správy front queue_t

Inicializácia fronty:

void qinit(queue_t *q);

Pole _q_stopper (ďalej ju budeme nazývať "stopor") sa inicializuje funkciou mblk_init(), jeho ukazovateľ predchádzajúceho prvku a nasledujúceho prvku sa nastavujú tak, aby ukazovali na neho samého. Čítač prvkov fronty sa nulová.

Pridanie nového prvku (správy):

void putq(queue_t *q, mblk_t *m);

Nový prvok m sa pridáva na koniec zoznamu, ukazovatele prvku sa nastavujú tak, aby stopor sa stal pre neho nasledujúcim prvkom a on pre stopor predchádzajúcim. Čítač prvkov fronty sa zvyšuje.

Odznenie prvku z fronty:

mblk_t * getq(queue_t *q); 

sa odstraňuje tá správa, ktorá stojí za stoporom, čítač prvkov sa znižuje. Ak vo fronte, okrem stopora, nie sú žiadne prvky, vracia sa 0.

Vloženie správy do fronty:

void insq(queue_t *q, mblk_t *emp, mblk_t *mp); 

Prvok mp sa vkladá pred prvok emp. Ak emp=0, správa sa pridáva na koniec fronty.

Odznenie správy z hlavy fronty:

void remq(queue_t *q, mblk_t *mp); 

Čítač prvkov sa znižuje.

Čítanie ukazovateľa na prvý prvok vo fronte:

mblk_t * peekq(queue_t *q); 

Odstránenie všetkých prvkov z fronty s odstránením samotných prvkov:

void flushq(queue_t *q, int how);

Argument how nie je používaný. Počítadlo prvkov fronty sa nastavuje na nulu.

Makro na čítanie ukazovateľa na posledný prvok fronty:

mblk_t * qlast(queue_t *q);

Pri práci s frontami správ je potrebné mať na pamäti, že pri volaní ms_queue_put(q, m) s nulovým ukazovateľom na správu sa funkcia zacyklí. Vaša aplikácia zamrzne. Podobne sa správa ms_queue_next(q, m).

Spojenie filtrov

Popísaná fronta sa používa na prenos správ z jedného filtra na druhý alebo z jedného do viacerých filtrov. Filtre a ich spojenia tvoria orientovaný graf. Vstup alebo výstup filtra budeme nazývať všeobecným termínom "pin". Na opísanie poradia spojení filtrov medzi sebou sa v mediastreameri používa pojem "signálny bod". Signálny bod je štruktúra _MSCPoint, ktorá obsahuje ukazovateľ na filter a číslo jedného z jeho pinov, čím opisuje spojenie jedného z vstupov alebo výstupov filtra.

Signálny bod grafu spracovania dát

typedef struct _MSCPoint{
struct _MSFilter *filter; // Ukazovateľ na filter mediastreamera.
int pin;                        // Číslo jedného z vstupov alebo výstupov filtra, t.j. pin.
} MSCPoint;

Piny filtrov sú číslované od nuly.

Spojenie dvoch pinov frontou správ je opísané štruktúrou _MSQueue, ktorá obsahuje frontu správ a ukazovatele na dva signálne body, ktoré spája:

typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;

Nazývame túto štruktúru signálnym linkom. Každý filter mediastreamera obsahuje tabuľku vstupných liniek a tabuľku výstupných liniek (MSQueue). Veľkosť tabuliek je určená pri vytváraní filtra, čo sme už robili pomocou exportovanej premennej typu MSFilterDesc, keď sme vyvíjali náš vlastný filter. Nižšie je zobrazená štruktúra opisujúca akýkoľvek filter v mediastreameri, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Ukazovateľ na popisovač filtra. */
    /* Chránené atribúty, ktoré nemožno presúvať ani odstraňovať, inak bude narušená práca s pluginmi. */
    ms_mutex_t lock;      /* Semafor. */
    MSQueue **inputs;     /* Tabuľka vstupných liniek. */
    MSQueue **outputs;    /* Tabuľka výstupných liniek. */
    struct _MSFactory *factory; /* Ukazovateľ na továreň, ktorá vytvorila tento príklad filtra. */
    void *padding;              /* Nepoužíva sa, bude zapojený, ak budú pridané chránené polia. */
    void *data;                 /* Ukazovateľ na ľubovoľnú štruktúru na uchovávanie dát vnútornej stavu filtra a medzi výpočtov. */
    struct _MSTicker *ticker;   /* Ukazovateľ na objekt tikera, ktorý by nemal byť nulový, keď sa volá funkcia process(). */
    /*súkromné atribúty, môžu sa presúvať a meniť kedykoľvek*/
    MSList *notify_callbacks; /* Zoznam spätných volaní, používaných na spracovanie udalostí filtra. */
    uint32_t last_tick;       /* Číslo posledného tiketu, keď bol vykonaný hovor process(). */
    MSFilterStats *stats;     /* Štatistika práce filtra.*/
    int postponed_task; /*Počet odložených úloh. Niektoré filtre môžu odkladať spracovanie dát (volanie process()) o niekoľko tikov.*/
    bool_t seen;  /* Príznak, ktorý používa tikar na označenie, že tento príklad filtra už bol spracovaný v danom tikete.*/
};
typedef struct _MSFilter MSFilter;

Po tom, čo sme vo svojej C-programu prepojili filtre podľa našich predstáv (ale neprepojili sme tikar), sme tým vytvorili orientovaný graf, ktorého uzly sú príklady štruktúry MSFilter, a hrany sú príklady liniek MSQueue.

Zákulisná činnosť tickeru

Keď som vám povedal, že tikar je filtrovaný zdroj tikotov, nebola to celá pravda o ňom. Tikar je objekt, ktorý spúšťa funkcie process() všetkých filtrov schémy (grafu), ku ktorej je pripojený. Keď v C-programu pripojíme tikar k filtru grafu, ukazujeme tikaru graf, ktorým bude od tohto momentu spravovať, kým ho neodpojíme. Po pripojení, tikar začína prezerať graf, ktorý mu bol zverený, vytvárajúc zoznam filtrov, do ktorých patrí. Aby sa zabránilo "počítaniu" rovnakého filtra dvakrát, označuje zistené filtre, nastavením v nich príznaku seen. Hľadanie sa vykonáva podľa tabuliek liniek, ktoré má každý filter.

Počas svojej oboznamovacej prehliadky grafu tick nadobúdá, či je medzi filtrami aspoň jeden, ktorý plní úlohu zdroja dátových blokov. Ak sa takýto nenájde, graf je považovaný za nesprávny a ticker nútene ukončí svoju činnosť.

Ak sa graf ukázal ako "správny", pre každý nájdený filter sa na inicializáciu vyvolá funkcia preprocess(). Akonáhle nastane čas na ďalší cyklus spracovania (predvolene každých 10 milisekúnd), tick vyvolá funkciu process() pre všetky predtým nájdené filtre zdrojov a potom aj pre ostatné filtre zo zoznamu. Ak filter má vstupné linky, tak spustenie funkcie process() sa opakuje, kým sa vstupné linky nevyprázdnia. Potom prejde k nasledujúcemu filtru zo zoznamu a "prehráva" ho, kým sa nevyskytnú voľné vstupné linky od správ. Ticker prechádza od filtra k filtru, kým sa neskončí zoznam. Týmto sa cyklus spracovania končí.

Teraz sa vrátime k n-ticím a prediskutujeme, na čo bola v mediastreameri pridaná takáto entita. Vo všeobecnosti objem dát potrebný algoritmu pôsobiacemu vo filtri nezhoduje a nie je krátky s veľkosťou dátových bufferov prichádzajúcich na vstupe. Napríklad píšeme filter, ktorý vykonáva rýchlu Fourierovu transformáciu, ktorá podľa definície môže spracovávať iba bloky dát, ktorých veľkosť je mocninou dvoch. Nech je to 512 vzoriek. Ak sú dáta generované telefónnym kanálom, buffer dát každej správy na vstupe bude prinášať po 160 vzorkách signálu. Existuje pokušenie neberúť dáta z vchodu, kým tam nebude požadované množstvo dát. Ale v tomto prípade vznikne kolízia s tickerom, ktorý bude neuspešne snažiť sa „prehrávať“ filter do vyprázdnenia vstupného linku. Predtým sme tento pravidlo označili ako tretí princíp fungovania filtra. Podľa tohto princípu funkcia process() filtra by mala zobrať všetky dáta z vstupných frontov.

Okrem toho z vstupu nebude možné získať iba 512 súčtov, pretože je možné zberať len celé bloky, t.j. filter bude musieť získať 640 súčtov a použitím 512 z nich zvyšok až do nahromadenia novej dávky údajov. Týmto spôsobom náš filter, okrem vykonávania svojej hlavnej úlohy, musí zabezpečiť pomocné činnosti na medziskladovanie vstupných údajov. Vývojári mediastreamera na vyriešenie tejto všeobecnej úlohy vyvinuli špeciálny objekt — MSBufferizer (bufferizátor), ktorý túto úlohu rieši pomocou n-tíc.

Buffer (MSBufferizer)

Tento objekt bude akumulovať vstupné dáta vo vnútri filtra a začne ich odovzdávať na spracovanie hneď, ako sa množstvo informácií stane dostatočným na spustenie algoritmu filtra. Kým bufferizátor hromadí dáta, filter bude fungovať v prázdnom režime, neutráciac výpočtovú kapacitu procesora. Ale hneď ako funkcia čítania z bufferizátora vráti hodnotu odlišnú od nuly, funkcia process() filtra začne získavať a spracovávať z bufferizátora dáta po dávkach potrebnej veľkosti, až do ich vyčerpania.
Nezvyčajné dáta zatiaľ zostanú v bufferizátore ako prvý prvok n-tice, ku ktorému sa pripoja nasledujúce bloky vstupných údajov.

Štruktúra, ktorá popisuje bufferizátor:

struct _MSBufferizer{
queue_t q; /* Fronta správ. */
int size; /* Celková veľkosť údajov nachádzajúcich sa v bufferizátore v danom momente. */
};
typedef struct _MSBufferizer MSBufferizer;

Funkcie správy MSBufferizer

Vytvorenie nového príkladu bufferizátora:

MSBufferizer * ms_bufferizer_new(void);

Pamäť sa vydelí, inicializuje v ms_bufferizer_init() a vráti ukazovateľ.

Funkcia inicializácie:

void ms_bufferizer_init(MSBufferizer *obj); 

Inicializuje sa fronta q, pole size nastaví sa na nulu.

Pridanie správy:

void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); 

Správa m sa pridáva do fronty. Vypočítaná veľkosť blokov údajov sa pridáva k size.

Presun všetkých správ z fronty správy linky do bufferizátora q:

void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   

Prenos správ z linky q do bufferizátora sa vykonáva pomocou funkcie ms_bufferizer_put().

Čítanie z bufferizátora:

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); 

Ak je veľkosť akumulovaných v bufferizátore údajov menšia ako požadovaná (datalen), funkcia vráti nulu, kopírovanie údajov do data sa neuskutoční. V opačnom prípade sa vykoná po sebe idúce kopírovanie údajov z n-tíc nachádzajúcich sa vo vyrovnávacej pamäti. Po skopírovaní sa n-tica odstráni a pamäť sa uvoľní. Kopírovanie sa skončí v okamihu, keď bude skopírovaných datalen bajtov. Ak sa miesto minie uprostred bloku údajov, v tejto správe sa blok údajov skráti na zostávajúcu nekopírovanú časť. Pri ďalšom volaní sa kopírovanie obnoví od tohto miesta.

Čítanie počtu údajov, ktoré sú momentálne dostupné vo vyrovnávacej pamäti:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Vracia pole size vyrovnávacej pamäte.

Zahodenie časti údajov nachádzajúcich sa vo vyrovnávacej pamäti:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Určené množstvo bajtov údajov sa extrahuje a zahodí. Vyhadzujú sa najstaršie údaje.

Odstránenie všetkých správ nachádzajúcich sa vo vyrovnávacej pamäti:

void ms_bufferizer_flush(MSBufferizer *obj); 

Počítadlo údajov sa resetuje na nulu.

Odstránenie všetkých správ nachádzajúcich sa vo vyrovnávacej pamäti:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Resetovanie počítadla sa neuskutoční.

Odstránenie vyrovnávacej pamäte a uvoľnenie pamäti:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Príklady použitia vyrovnávacej pamäte nájdete v zdrojovom kóde niekoľkých filtrov mediastreamera. Napríklad vo filtri MS_L16_ENC, ktorý vykonáva preusporiadanie bajtov v odmeraných hodnotách zo sieťového poriadku do poriadku hostiteľa: l16.c

V nasledujúcom článku preskúmame otázku hodnotenia zaťaženia na ticker a spôsoby boja s nadmernou výpočtovou záťažou v mediastreameri.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster