Examinăm motorul VoIP Mediastreamer2. Partea 11

Materialul articolului provine de pe canalul meu zen.

Examinăm motorul VoIP Mediastreamer2. Partea 11

Mecanism de transfer de date

  • Bloc de date dblk_t
  • Mesaj mblk_t
  • Funcții pentru manipularea mesajelor mblk_t
  • Coada queue_t
  • Funcții pentru manipularea cozilor queue_t
  • Conectarea filtrelor
  • Punctul de semnalizare al graficului de procesare a datelor
  • Activitate de culise a ticker-ului
  • Buffer (MSBufferizer)
  • Funcții pentru manipularea MSBufferizer

În trecut, pe care l-ați citit Am dezvoltat propriul nostru filtru. Această articolă va fi dedicată structurii mecanismului intern de transfer de date între filtrele de streaming media. Aceasta va permite ulterior scrierea de filtre sofisticate cu mai puțin efort.

Mecanism de transfer de date

Transferul de date în streaming media se realizează prin intermediul cozilor descrise de structura queue_t. Datele sunt transferate prin cozi sub formă de serii de mesaje de tipul mblk_t, care în sine nu conțin datele semnalului, ci doar referințe la mesajul anterior, următor și la blocul de date. În plus, vreau să subliniez în mod special că există un câmp pentru referința la un mesaj de același tip, care permite organizarea unei liste legate din mesaje. Grupul de mesaje unite de această listă va fi numit tuplu. Astfel, orice element al cozii poate fi un mesaj singular mblk_t, sau poate fi capul tuplului de mesaje mblk_t. Fiecare mesaj din tuplu poate avea propriul său bloc de date asociat. De ce avem nevoie de tuple, vom discuta puțin mai târziu.

Așa cum s-a menționat mai sus, mesajul în sine nu conține bloc de date, ci doar un pointer către zona de memorie unde este stocat blocul. În această parte, imaginea generală a funcționării streaming-ului media amintește de un depozit de uși din desenele animate "Compania Monstrilor", unde ușile (referințele la date - camere) se deplasează cu o viteză sfidătoare pe conveioare suspendate, în timp ce camerele în sine rămân nemișcate.

Acum, mergând pe ierarhie de la jos în sus, să analizăm în detaliu entitățile enumerate ale mecanismului de transfer de date în streaming media.

Bloc de date dblk_t

Blocul de date constă dintr-un header și un buffer de date. Header-ul este descris de următoarea structură,

typedef struct datab
{
unsigned char *db_base; // Pointer la începutul buffer-ului de date.
unsigned char *db_lim;  // Pointer la sfârșitul buffer-ului de date.
void (*db_freefn)(void*); // Funcția de eliberare a memoriei la ștergerea blocului.
int db_ref; // Contor de referințe.
} dblk_t;

Câmpurile structurii conțin pointere către începutul și sfârșitul bufferului, precum și funcția de eliminare a bufferului de date. Ultimul element din antet db_ref — contor de referințe, dacă ajunge la zero, acesta servește drept semnal pentru a șterge acest bloc din memorie. Dacă blocul de date a fost creat prin funcția datab_alloc() , atunci bufferul de date va fi plasat în memorie imediat după antet. În toate celelalte cazuri, bufferul poate fi situat separat. În bufferul de date vor fi plasate semnalele sau alte date pe care dorim să le procesăm prin filtre.

Un nou exemplu de bloc de date este creat prin funcția:

dblk_t *datab_alloc(int size);

Ca parametru de intrare, îi este transmis dimensiunea datelor pe care le va stoca blocul. Se alocă mai multă memorie pentru a plasa la începutul memoriei alocate antetul — structura datab. Dar, în utilizarea altor funcții, acest lucru nu se întâmplă întotdeauna, în unele cazuri bufferul de date poate fi situat separat de antetul blocului de date. Câmpurile structurii sunt configurate în momentul creării astfel încât câmpul său db_base să indice începutul zonei de date, iar db_lim să indice sfârșitul. Contorul de referințe db_ref este setat la unu. Pointerul funcției de curățare a datelor este setat la zero.

Mesaj mblk_t

După cum s-a menționat, elementele cozii au tipul mblk_t, care este definit după cum urmează:

typedef struct msgb
{
  struct msgb *b_prev;   // Pointer către elementul anterior al listei.
  struct msgb *b_next;   // Pointer către elementul următor al listei.
  struct msgb *b_cont;   // Pointer pentru lipirea altor mesaje la mesaj, pentru a crea un tuple de mesaje.
  struct datab *b_datap; // Pointer la structura blocului de date.
  unsigned char *b_rptr; // Pointer la începutul zonei de date pentru citirea datelor din bufferul b_datap.
  unsigned char *b_wptr; // Pointer la începutul zonei de date pentru scrierea datelor în bufferul b_datap.
  uint32_t reserved1;    // Câmp rezervat1, mediastreamer-ul plasează informații administrative acolo. 
  uint32_t reserved2;    // Câmp rezervat2, mediastreamer-ul plasează informații administrative acolo.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Structura mblk_t la început conține pointere b_prev, b_next, care sunt necesare pentru organizarea listei dublu înlănțuite (din care face parte coada queue_t).

Apoi urmează pointerul b_cont, care este utilizat doar atunci când un mesaj intră în tuplu. Pentru ultimul mesaj din tuplu, acest indicator rămâne nul.

Mai departe, vedem un indicator către blocul de date b_datap, pentru care există mesajul. După acesta urmează indicatorii, către zona din interiorul buffer-ului de date al blocului. Câmpul b_rptr indică locul de unde vor fi citite datele din buffer. Câmpul b_wptr indică locul de unde vor fi scrise date în buffer.

Câmpurile rămase au caracter de serviciu și nu se referă la funcționarea mecanismului de transfer de date.

Mai jos este prezentat un mesaj singular cu numele m1 și blocul de date d1.
Examinăm motorul VoIP Mediastreamer2. Partea 11
În desenul următor este ilustrat un tuplu din trei mesaje m1, m1_1, m1_2.
Examinăm motorul VoIP Mediastreamer2. Partea 11

Funcțiile pentru gestionarea mesajelor mblk_t

Un mesaj nou mblk_t este creat de funcția:

mblk_t *allocb(int size, int pri); 

aceasta alocă în memorie un mesaj nou mblk_t cu un bloc de date de dimensiunea specificată dimensiune, al doilea argument — pri nu este utilizat în versiunea discutată a bibliotecii. Acesta ar trebui să rămână nul. În cursul funcției, va fi alocată memorie pentru structura noului mesaj și va fi apelată funcția mblk_init(), care va inițializa toate câmpurile instanței create a structurii și apoi, cu ajutorul celei menționate anterior, datab_alloc(), va crea un buffer de date. După care vor fi configurate câmpurile din structură:

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

La ieșire obținem un mesaj nou cu câmpurile inițializate și un buffer de date gol. Pentru a adăuga date în mesaj, trebuie să le copiem în buffer-ul blocului:

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

unde data — un pointer către sursa de date, iar dimensiune — dimensiunea acestora.
Apoi trebuie actualizat pointer-ul pe punctul de scriere, astfel încât să indice din nou începutul zonei libere din buffer:

msg->b_wptr = msg->b_wptr + size

Dacă este necesar să se creeze un mesaj dintr-un buffer existent, fără copiere, atunci se utilizează funcția:

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

Funcția, după crearea mesajului și a structurii blocului de date, va configura indicatorii săi către date la adresa buf. Adică, în acest caz, buffer-ul de date nu se află imediat după câmpurile antetului blocului de date, așa cum a fost la crearea blocului de date prin funcție. datab_alloc()Bufferul de date transmis funcției va rămâne acolo unde a fost, dar cu ajutorul pointerilor va fi legat de antetul recent creat al blocului de date, iar acesta respectiv la mesaj.

Pentru un mesaj mblk_t pot fi atașate secvențial mai multe blocuri de date. Acest lucru se face prin funcția:

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

mp — mesajul căruia i se va adăuga un alt bloc de date;
data — pointerul la bloc, o copie a căruia va fi adăugată la mesaj;
dimensiune — dimensiunea datelor;
pad — flagul că dimensiunea memoriei alocate trebuie să fie aliniată la limita de 4 octeți (completarea se va face cu zero-uri).

Dacă în bufferul existent al mesajului există suficient spațiu, atunci noile date vor fi lipite la datele deja existente. Dacă spațiul liber în bufferul de date al mesajului este mai mic decât dimensiune, atunci se creează un mesaj nou, cu o dimensiune suficientă a bufferului, iar datele sunt copiate în bufferul său. Acest mesaj nou este atașat la cel original cu ajutorul pointerului b_cont. În acest caz, mesajul devine un tuplu.

Dacă în tuplu trebuie adăugat un alt bloc de date, atunci trebuie folosită funcția:

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

aceasta va găsi ultimul mesaj din tuplu (acesta va fi b_cont va fi nul) și va apela funcția appendb().

Pentru a afla dimensiunea datelor din mesaj sau din tuplu, se poate folosi funcția:

int msgdsize(const mblk_t *mp);

acesta va parcurge toate mesajele din tuplu și va returna cantitatea totală de date din bufferii de date ai acestor mesaje. Pentru fiecare mesaj, cantitatea de date este calculată astfel:

 mp->b_wptr - mp->b_rptr

Pentru a concatena două tuple se folosește funcția:

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

aceasta va atașa tuplul newm la coada tuplului mp și va returna pointerul la ultimul mesaj al tuplului rezultat.

Dacă este necesar, tuplul poate fi transformat într-un singur mesaj cu un bloc unic de date, acest lucru se face cu funcția:

void msgpullup(mblk_t *mp,int len);

dacă argumentul len este egal cu -1, atunci dimensiunea bufferului alocat este determinată automat. Dacă len dacă este un număr pozitiv, atunci va fi creat un buffer de această dimensiune și în el vor fi copiate datele mesajelor tuple. Dacă bufferul se umple, copierea se va opri. Primul mesaj al tuplei va primi un buffer de o nouă dimensiune cu datele copiate. Celelalte mesaje vor fi șterse, iar memoria va fi returnată în heap.

La ștergerea structurii mblk_t se ia în considerare numărul de referințe al blocului de date, dacă la apelul freeb() ajunge la zero, atunci bufferul de date este șters împreună cu instanța mblk_t, la care se referă.

Inițializarea câmpurilor unui nou mesaj:

void mblk_init(mblk_t *mp);

Adăugarea unei noi porții de date în mesaj:

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

Dacă noile date nu încap în spațiul liber al bufferului de date al mesajului, atunci la mesaj se atașează un mesaj creat separat cu bufferul de dimensiunea necesară (în primul mesaj se stabilește un pointer către mesajul adăugat), mesajul se transformă într-o tuplă.

Adăugarea unei porții de date în tuplă:

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

Funcția apelează appendb() într-un ciclu.

Combinarea a două tuple într-una:

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

Mesaj newm va fi atașat la mp.

Crearea unei copii a unui mesaj unic:

mblk_t *copyb(const mblk_t *mp);

Copierea completă a tuplei cu toate blocurile de date:

mblk_t *copymsg(const mblk_t *mp);

Elementele tuplei sunt copiate în acest fel de funcția copyb().

Crearea unei copii ușoare a mesajului. mblk_tÎn acest caz, blocul de date nu este copiat, ci se mărește numărul său de referințe. db_ref:

mblk_t *dupb(mblk_t *mp);

Crearea unei copii ușoare a tuplei. Blocurile de date nu sunt copiate, doar se măresc numerele lor de referințe. db_ref:

mblk_t *dupmsg(mblk_t* m);

Concatenarea tuturor mesajelor din tuplă într-un singur mesaj:

void msgpullup(mblk_t *mp,size_t len);

Dacă argumentul len este -1, atunci dimensiunea bufferului rezervat este determinată automat.

Ștergerea mesajului, tuplei:

void freemsg(mblk_t *mp);

Numărul de referințe al blocului de date este redus cu o unitate. Dacă ajunge la zero, atunci blocul de date este, de asemenea, șters.

Calcularea volumului total de date din mesaj sau tuplă.

size_t msgdsize(const mblk_t *mp);

Extrașionarea mesajului din coada din spate:

mblk_t *ms_queue_peek_last (q);

Copierea conținutului câmpurilor rezervate ale unui mesaj în alt mesaj (de fapt, în aceste câmpuri se află flag-uri care sunt utilizate de media streamer):

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

Coadă queue_t

Coada de mesaje în mediastreamer este implementată ca o listă dublu înlănțuită circulată. Fiecare element al listei conține un pointer către un bloc de date cu măsurători ale semnalului. Astfel, se deplasează doar pointerii cătreblocuri de date, în timp ce datele în sine rămân immobile. Cu alte cuvinte, se mișcă doar referințele la acestea.
Structura care descrie coada queue_t, este prezentată mai jos:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Element" gol al cozii, nu indică asupra datelor, este utilizat doar pentru gestionarea cozii. La inițializarea cozii (qinit()), pointerii săi sunt setați astfel încât să indice asupra lui însuși. */
   int q_mcount;        // Numărul de elemente în coadă.
} queue_t;

Structura conține un câmp - pointer _q_stopper de tip *mblk_t, care indică asupra primului element (mesaj) din coadă. Al doilea câmp al structurii este un contor al mesajelor aflate în coadă.
În figura de mai jos este prezentată coada cu numele q1, care conține 4 mesaje m1, m2, m3, m4.
Examinăm motorul VoIP Mediastreamer2. Partea 11
În figura următoare este prezentată coada cu numele q1, care conține 4 mesaje m1, m2, m3, m4. Mesajul m2 este capul tuplului, în care se află și două alte mesaje m2_1 și m2_2.

Examinăm motorul VoIP Mediastreamer2. Partea 11

Funcții pentru manipularea cozilor queue_t

Inițializarea cozii:

void qinit(queue_t *q);

Câmp _q_stopper (în continuare îl vom numi "stop") este inițializat de funcția mblk_init(), pointerul său către elementul anterior și cel următor sunt setați astfel încât să se indică asupra lui însuși. Contorul elementelor din coadă este resetat.

Adăugarea unui nou element (mesaj):

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

Un nou element m este adăugat la sfârșitul listei, pointerii elementului sunt setați astfel încât stop să devină pentru el următorul element, iar el pentru stop anteriorul. Contorul elementelor cozii este incrementat.

Extracția unui element din coadă:

mblk_t * getq(queue_t *q); 

se extrage acel mesaj care se află după stop, contorul elementelor este decrementat. Dacă în coadă, în afară de stop, nu mai sunt alte elemente, se returnează 0.

Inserarea unui mesaj în coadă:

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

Elementul mp se inserează înainte de elementul emp. Dacă emp=0, astfel mesajul este adăugat la coada cozii.

Extracția mesajului din capul cozii:

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

Contorul elementelor este decrementat.

Citirea pointerului către primul element din coadă:

mblk_t * peekq(queue_t *q); 

Ștergerea tuturor elementelor din coadă cu ștergerea elementelor în sine:

void flushq(queue_t *q, int how);

Argumentul how nu este utilizat. Contorul elementelor din coadă este setat pe zero.

Macro pentru citirea pointerului la ultimul element din coadă:

mblk_t * qlast(queue_t *q);

Când se lucrează cu cozile de mesaje, trebuie avut în vedere că, la apelul ms_queue_put(q, m) cu un pointer nul la mesaj, funcția intră într-o buclă. Programul dvs. va rămâne blocat. Similar se comportă ms_queue_next(q, m).

Conectarea filtrelor

Coadă descrisă mai sus este utilizată pentru a transmite mesaje de la un filtru la altul sau de la un filtru la mai multe filtre. Filtrele și conexiunile lor formează un graf orientat. Intrarea sau ieșirea unui filtru va fi numită termen general "pin". Pentru a descrie ordinea conexiunilor între filtre, în media streamer se folosește conceptul de "punct de semnal". Un punct de semnal este o structură _MSCPoint, care conține un pointer către filtru și numărul unuia dintre pinurile sale, de aceea descrie conexiunea uneia dintre intrările sau ieșirile filtrului.

Punctul de semnalizare al graficului de procesare a datelor

typedef struct _MSCPoint{
struct _MSFilter *filter; 
int pin;                        
} MSCPoint;

Pinurile filtrelor sunt numerotate începând de la zero.

Conexiunea dintre două pinuri printr-o coadă de mesaje este descrisă de structură _MSQueue, care conține coada de mesaje și pointere către două puncte de semnal, pe care le conectează:

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

Vom numi această structură link de semnal. Fiecare filtru media streamer conține o tabelă de linkuri de intrare și o tabelă de linkuri de ieșire (MSQueue). Dimensiunea tabelelor este setată la crearea filtrului, am făcut deja acest lucru cu ajutorul unei variabile exportate de tip MSFilterDesc, atunci când am dezvoltat propriul nostru filtru. Mai jos este prezentată structura care descrie orice filtru din media streamer, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Pointer to the filter descriptor. */
    /* Protected attributes, they cannot be moved or removed, otherwise functionality with plugins will be disrupted. */
    ms_mutex_t lock;      /* Semaphore. */
    MSQueue **inputs;     /* Input links table. */
    MSQueue **outputs;    /* Output links table. */
    struct _MSFactory *factory; /* Pointer to the factory that created this filter instance. */
    void *padding;              /* Not used, will be employed if protected fields are added. */
    void *data;                 /* Pointer to an arbitrary structure for storing the filter's internal state and intermediate calculations. */
    struct _MSTicker *ticker;   /* Pointer to the ticker object, which must not be null when the process() function is called. */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* List of callbacks used for processing filter events. */
    uint32_t last_tick;       /* The number of the last tick when the process() call was executed. */
    MSFilterStats *stats;     /* Filter performance statistics.*/
    int postponed_task; /*Number of postponed tasks. Some filters may delay data processing (process() call) for several ticks.*/
    bool_t seen;  /* Flag used by the ticker to mark that this filter instance has already been serviced during this tick.*/
};
typedef struct _MSFilter MSFilter;

După ce am conectat filtrele în programul C conform planului nostru (dar nu am conectat tickerul), astfel am creat un graf orientat, al cărui noduri sunt instanțele structurilor MSFilter, iar muchiile sunt instanțele legăturilor MSQueue.

Activitate de culise a ticker-ului

Când v-am spus că tickerul este un filtru sursă de ritmuri, nu am spus întreaga adevăr despre el. Tickerul este un obiect care declanșează funcțiile la intervale regulate process() ale tuturor filtrelor din schema (graf) la care este conectat. Când conectăm tickerul la filtru în programul C, arătăm tickerului graful pe care îl va gestiona de acum încolo, până când îl deconectăm. După conectare, tickerul începe să examineze graful încredințat lui, creând o listă a filtrelor incluse în el. Pentru a nu "număra" același filtru de două ori, acesta marchează filtrele descoperite, setându-le semnul seen. Căutarea se realizează prin tabelele legăturilor, care există pe fiecare filtru.

În timpul turului său de documentare, tickerul verifică dacă există printre filtre măcar unul care joacă rolul unei surse de blocuri de date. Dacă nu se găsesc astfel de filtre, graficul este considerat incorect și tickerul se închide în mod abrupt.

Dacă graficul s-a dovedit a fi "corect", pentru fiecare filtru găsit, se apelează funcția de inițializare preprocess(). Atunci când vine momentul pentru următoarea etapă de procesare (de obicei la fiecare 10 milisecunde), tickerul apelează funcția process() pentru toate filtrele sursă găsite anterior, și apoi pentru celelalte filtre din listă. Dacă filtru are legături de intrare, atunci apelarea funcției process() se repetă până când cozile legăturilor de intrare sunt goale. După aceea, trece la următorul filtru din listă și "răsfoiește" până când legăturile de intrare sunt eliberate de mesaje. Tickerul trece de la un filtru la altul până când lista se termină. Astfel, procesarea etapei se finalizează.

Acum ne vom întoarce la tupluri și vom discuta despre motivul pentru care o astfel de entitate a fost adăugată în mediastreamer. În general, volumul de date necesar algoritmului care funcționează în cadrul filtrului nu coincide și nu este un multiplu al dimensiunii bufferelor de date care intră. De exemplu, scriem un filtru care efectuează o transformare rapidă Fourier, care prin definiție poate procesa doar blocuri de date ale căror dimensiuni sunt puteri ale lui 2. Să presupunem că este vorba de 512 eșantioane. Dacă datele sunt generate de un canal telefonic, atunci bufferul de date al fiecărui mesaj de intrare va aduce 160 eșantioane de semnal. Există tentația de a nu lua date de pe intrare până nu avem cantitatea necesară de date. Dar, în acest caz, va apărea o coliziune cu tickerul, care va încerca fără succes să răsfoiască filtrul până când linia de intrare este golită. Anterior am denumit această regulă ca fiind al treilea principiu de funcționare al filtrului. Conform acestui principiu, funcția process() a filtrului trebuie să preia toate datele din cozile de intrare.

În plus, nu se pot prelua doar 512 înregistrări de la intrare, deoarece se pot lua doar blocuri întregi, adică filtrul va trebui să preia 640 de înregistrări și, utilizând 512 dintre ele, restul va rămâne până la acumularea unei noi porții de date. Astfel, filtrul nostru, pe lângă activitatea sa principală, trebuie să asigure acțiuni auxiliare pentru stocarea intermediară a datelor de intrare. Dezvoltatorii mediastreamerului, pentru a rezolva această problemă comună, au dezvoltat un obiect special – MSBufferizer (bufferizer), care rezolvă această problemă prin intermediul tuplurilor.

Buffer (MSBufferizer)

Acesta este un obiect care va acumula datele de intrare în cadrul filtrului și le va începe să le furnizeze pentru procesare, odată ce cantitatea de informații devine suficientă pentru a rula algoritmul filtrului. În timp ce bufferizerul acumulează date, filtrul va funcționa în modul gol, fără a consuma puterea de calcul a procesorului. Dar, odată ce funcția de citire din bufferizer returnează o valoare diferită de zero, funcția process() a filtrului începe să preia și să proceseze datele din bufferizer în porții de dimensiunea necesară, până la epuizarea acestora.
Datele care nu sunt utilizate rămân în bufferizer ca prim element al tuplului, la care se atașează blocurile ulterioare de date de intrare.

Structura care descrie bufferizerul:

struct _MSBufferizer{
queue_t q; /* Coada mesajelor. */
int size; /* Dimensiunea totală a datelor aflate în bufferizer în acest moment. */
};
typedef struct _MSBufferizer MSBufferizer;

Funcții pentru manipularea MSBufferizer

Crearea unei noi instanțe a bufferizerului:

MSBufferizer * ms_bufferizer_new(void);

Se alocă memorie, se inițializează în ms_bufferizer_init() și se returnează un pointer.

Funcția de inițializare:

void ms_bufferizer_init(MSBufferizer *obj); 

Se inițiază coada q, câmp dimensiune se setează la zero.

Adăugarea unui mesaj:

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

Mesajul m este adăugat în coadă. Dimensiunea calculată a blocurilor de date este adăugată la dimensiune.

Traducea în bufferizer a tuturor mesajelor din coada de date a link-ului q:

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

Transferul mesajelor din link q în bufferizer se efectuează cu ajutorul funcției ms_bufferizer_put().

Citirea din bufferizer:

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

Dacă dimensiunea datelor acumulate în bufferizer este mai mică decât cerută (datalen), funcția returnează zero, copierea datelor în data nu se efectuează. În caz contrar, se efectuează copierea secvențială a datelor din tuplurile aflate în buffer. După copiere, tuplul este șters și memoria eliberată. Copierea se încheie în momentul în care sunt copiate datalen octeți. Dacă spațiul se termină în mijlocul unui bloc de date, atunci în acest mesaj, blocul de date va fi redus la partea rămasă necopiată. La următoarea apelare, copierea va continua de la acest loc.

Citirea numărului de date disponibile în acest moment în buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Returnează câmpul dimensiune buffer-ului.

Scoaterea unei părți din datele aflate în buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Numărul specificat de octeți de date este extras și ignorat. Cele mai vechi date sunt eliminate.

Ștergerea tuturor mesajelor aflate în buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Contorul de date este resetat la zero.

Ștergerea tuturor mesajelor aflate în buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Resetarea contorului nu se efectuează.

Ștergerea buffer-ului și eliberarea memoriei:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Exemple de utilizare a buffer-ului pot fi găsite în codul sursă al mai multor filtre de medistreamer. De exemplu, în filtrul MS_L16_ENC, care efectuează permutarea octeților în eșantioane din ordinea rețelei în ordinea gazdă: l16.c

În articolul următor, vom examina problema evaluării sarcinii pe ticker și metodele de combatere a supraîncărcării computaționale în mediastreamer.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster