{"id":79540,"date":"2020-04-28T07:42:01","date_gmt":"2020-04-28T05:42:01","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11"},"modified":"2020-04-28T07:42:01","modified_gmt":"2020-04-28T05:42:01","slug":"izuchaem-voip-dvizhok-mediastreamer2-chast-11","status":"publish","type":"post","link":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","title":{"rendered":"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Materialul articolului provine de pe canalul meu <noindex><a rel=\"nofollow\" href=\"https:\/\/zen.yandex.ru\/profile\/editor\/id\/5e3f8e0751f5346faab4fc7a\">zen<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11\" src=\"\/wp-content\/uploads\/2020\/04\/b66fab43eb45c626c50f0383dcfa7cca.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mecanism de transfer de date<\/p>\n<p><\/p>\n<ul>\n<li>Bloc de date dblk_t<\/li>\n<li>Mesaj mblk_t<\/li>\n<li>Func\u021bii pentru manipularea mesajelor mblk_t<\/li>\n<li>Coada queue_t<\/li>\n<li>Func\u021bii pentru manipularea cozilor queue_t<\/li>\n<li>Conectarea filtrelor<\/li>\n<li>Punctul de semnalizare al graficului de procesare a datelor<\/li>\n<li>Activitate de culise a ticker-ului<\/li>\n<li>Buffer (MSBufferizer)<\/li>\n<li>Func\u021bii pentru manipularea MSBufferizer<\/li>\n<\/ul>\n<p><\/p>\n<p>\u00cen trecut, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/497932\/\">pe care l-a\u021bi citit<\/a><\/noindex> Am dezvoltat propriul nostru filtru. Aceast\u0103 articol\u0103 va fi dedicat\u0103 structurii mecanismului intern de transfer de date \u00eentre filtrele de streaming media. Aceasta va permite ulterior scrierea de filtre sofisticate cu mai pu\u021bin efort.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"mehanizm-peremescheniya-dannyh\">Mecanism de transfer de date<\/h2>\n<p><\/p>\n<p>Transferul de date \u00een streaming media se realizeaz\u0103 prin intermediul cozilor descrise de structura <em>queue_t<\/em>. Datele sunt transferate prin cozi sub form\u0103 de serii de mesaje de tipul <em>mblk_t<\/em>, care \u00een sine nu con\u021bin datele semnalului, ci doar referin\u021be la mesajul anterior, urm\u0103tor \u0219i la blocul de date. \u00cen plus, vreau s\u0103 subliniez \u00een mod special c\u0103 exist\u0103 un c\u00e2mp pentru referin\u021ba la un mesaj de acela\u0219i tip, care permite organizarea unei liste legate din mesaje. Grupul de mesaje unite de aceast\u0103 list\u0103 va fi numit tuplu. Astfel, orice element al cozii poate fi un mesaj singular <em>mblk_t<\/em>, sau poate fi capul tuplului de mesaje <em>mblk_t<\/em>. Fiecare mesaj din tuplu poate avea propriul s\u0103u bloc de date asociat. De ce avem nevoie de tuple, vom discuta pu\u021bin mai t\u00e2rziu.<\/p>\n<p><\/p>\n<p>A\u0219a cum a fost men\u021bionat anterior, mesajul \u00een sine nu con\u021bine un bloc de date, ci doar un indicator c\u0103tre zona de memorie \u00een care este stocat blocul. \u00cen aceast\u0103 parte, imaginea general\u0103 a func\u021bion\u0103rii mediastreamer-ului seam\u0103n\u0103 cu o depozitare de u\u0219i din desenul animat \u201eMonsters, Inc.\u201d, \u00een care u\u0219ile (leg\u0103turi c\u0103tre date \u2014 camere) se mi\u0219c\u0103 cu o vitez\u0103 nebun\u0103 pe conveioare suspendate, \u00een timp ce camerele r\u0103m\u00e2n nemi\u0219cate. <\/p>\n<p><\/p>\n<p>Acum, merg\u00e2nd pe ierarhie de la jos \u00een sus, s\u0103 analiz\u0103m \u00een detaliu entit\u0103\u021bile enumerate ale mecanismului de transfer de date \u00een streaming media.<\/p>\n<p><\/p>\n<h3 id=\"blok--dannyh-dblk_t\">Bloc de date <em>dblk_t<\/em><\/h3>\n<p><\/p>\n<p>Blocul de date const\u0103 dintr-un header \u0219i un buffer de date. Header-ul este descris de urm\u0103toarea structur\u0103,<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct datab\n{\nunsigned char *db_base; \/\/ Pointer la \u00eenceputul buffer-ului de date.\nunsigned char *db_lim;  \/\/ Pointer la sf\u00e2r\u0219itul buffer-ului de date.\nvoid (*db_freefn)(void*); \/\/ Func\u021bia de eliberare a memoriei la \u0219tergerea blocului.\nint db_ref; \/\/ Contor de referin\u021be.\n} dblk_t;<\/code><\/pre>\n<p><\/p>\n<p>C\u00e2mpurile structurii con\u021bin pointere c\u0103tre \u00eenceputul \u0219i sf\u00e2r\u0219itul bufferului, precum \u0219i func\u021bia de eliminare a bufferului de date. Ultimul element din antet <em>db_ref<\/em> \u2014 contor de referin\u021be, dac\u0103 ajunge la zero, acesta serve\u0219te drept semnal pentru a \u0219terge acest bloc din memorie. Dac\u0103 blocul de date a fost creat prin func\u021bia <em>datab_alloc() <\/em>, atunci bufferul de date va fi plasat \u00een memorie imediat dup\u0103 antet. \u00cen toate celelalte cazuri, bufferul poate fi situat separat. \u00cen bufferul de date vor fi plasate semnalele sau alte date pe care dorim s\u0103 le proces\u0103m prin filtre.<\/p>\n<p><\/p>\n<p>Un nou exemplu de bloc de date este creat prin func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">dblk_t *datab_alloc(int size);<\/code><\/pre>\n<p><\/p>\n<p>Ca parametru de intrare, \u00eei este transmis dimensiunea datelor pe care le va stoca blocul. Se aloc\u0103 mai mult\u0103 memorie pentru a plasa la \u00eenceputul memoriei alocate antetul \u2014 structura <em>datab<\/em>. Dar, \u00een utilizarea altor func\u021bii, acest lucru nu se \u00eent\u00e2mpl\u0103 \u00eentotdeauna, \u00een unele cazuri bufferul de date poate fi situat separat de antetul blocului de date. C\u00e2mpurile structurii sunt configurate \u00een momentul cre\u0103rii astfel \u00eenc\u00e2t c\u00e2mpul s\u0103u <em>db_base<\/em> s\u0103 indice \u00eenceputul zonei de date, iar <em>db_lim<\/em> s\u0103 indice sf\u00e2r\u0219itul. Contorul de referin\u021be <em>db_ref<\/em> este setat la unu. Pointerul func\u021biei de cur\u0103\u021bare a datelor este setat la zero.<\/p>\n<p><\/p>\n<h3 id=\"soobschenie-mblk_t\">Mesaj <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Dup\u0103 cum s-a men\u021bionat, elementele cozii au tipul <em>mblk_t,<\/em> care este definit dup\u0103 cum urmeaz\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct msgb\n{\n  struct msgb *b_prev;   \/\/ Pointer c\u0103tre elementul anterior al listei.\n  struct msgb *b_next;   \/\/ Pointer c\u0103tre elementul urm\u0103tor al listei.\n  struct msgb *b_cont;   \/\/ Pointer pentru lipirea altor mesaje la mesaj, pentru a crea un tuple de mesaje.\n  struct datab *b_datap; \/\/ Pointer la structura blocului de date.\n  unsigned char *b_rptr; \/\/ Pointer la \u00eenceputul zonei de date pentru citirea datelor din bufferul b_datap.\n  unsigned char *b_wptr; \/\/ Pointer la \u00eenceputul zonei de date pentru scrierea datelor \u00een bufferul b_datap.\n  uint32_t reserved1;    \/\/ C\u00e2mp rezervat1, mediastreamer-ul plaseaz\u0103 informa\u021bii administrative acolo. \n  uint32_t reserved2;    \/\/ C\u00e2mp rezervat2, mediastreamer-ul plaseaz\u0103 informa\u021bii administrative acolo.\n  #if defined(ORTP_TIMESTAMP)\n  struct timeval timestamp;\n  #endif\n  ortp_recv_addr_t recv_addr;\n} mblk_t;<\/code><\/pre>\n<p><\/p>\n<p>Structura <em>mblk_t<\/em> la \u00eenceput con\u021bine pointere <em>b_prev<\/em>, <em>b_next<\/em>, care sunt necesare pentru organizarea listei dublu \u00eenl\u0103n\u021buite (din care face parte coada <em>queue_t<\/em>). <\/p>\n<p><\/p>\n<p>Apoi urmeaz\u0103 pointerul <em>b_cont<\/em>, care este utilizat doar atunci c\u00e2nd un mesaj intr\u0103 \u00een tuplu. Pentru ultimul mesaj din tuplu, acest indicator r\u0103m\u00e2ne nul. <\/p>\n<p><\/p>\n<p>Mai departe, vedem un indicator c\u0103tre blocul de date <em>b_datap<\/em>, pentru care exist\u0103 mesajul. Dup\u0103 acesta urmeaz\u0103 indicatorii, c\u0103tre zona din interiorul buffer-ului de date al blocului. C\u00e2mpul <em>b_rptr<\/em> indic\u0103 locul de unde vor fi citite datele din buffer. C\u00e2mpul <em>b_wptr<\/em> indic\u0103 locul de unde vor fi scrise date \u00een buffer. <\/p>\n<p><\/p>\n<p>C\u00e2mpurile r\u0103mase au caracter de serviciu \u0219i nu se refer\u0103 la func\u021bionarea mecanismului de transfer de date.<\/p>\n<p><\/p>\n<p>Mai jos este prezentat un mesaj singular cu numele <em>m1<\/em> \u0219i blocul de date <em>d1<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11\" src=\"\/wp-content\/uploads\/2020\/04\/e6a726b13491832258c1228f03bde802.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n\u00cen desenul urm\u0103tor este ilustrat un tuplu din trei mesaje <em>m1<\/em>, <em>m1_1<\/em>, <em>m1_2<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11\" src=\"\/wp-content\/uploads\/2020\/04\/9613feefe9684b9551cd98115faa9a08.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-soobscheniyami--mblk_t\">Func\u021biile pentru gestionarea mesajelor <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Un mesaj nou <em>mblk_t<\/em> este creat de func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *allocb(int size, int pri); <\/code><\/pre>\n<p><\/p>\n<p>aceasta aloc\u0103 \u00een memorie un mesaj nou <em>mblk_t<\/em> cu un bloc de date de dimensiunea specificat\u0103 <em>dimensiune<\/em>, al doilea argument \u2014 <em>pri<\/em> nu este utilizat \u00een versiunea discutat\u0103 a bibliotecii. Acesta ar trebui s\u0103 r\u0103m\u00e2n\u0103 nul. \u00cen cursul func\u021biei, va fi alocat\u0103 memorie pentru structura noului mesaj \u0219i va fi apelat\u0103 func\u021bia <em>mblk_init()<\/em>, care va ini\u021bializa toate c\u00e2mpurile instan\u021bei create a structurii \u0219i apoi, cu ajutorul celei men\u021bionate anterior, <em>datab_alloc()<\/em>, va crea un buffer de date. Dup\u0103 care vor fi configurate c\u00e2mpurile din structur\u0103: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mp-&gt;b_datap=datab;\nmp-&gt;b_rptr=mp-&gt;b_wptr=datab-&gt;db_base;\nmp-&gt;b_next=mp-&gt;b_prev=mp-&gt;b_cont=NULL;<\/code><\/pre>\n<p><\/p>\n<p>La ie\u0219ire ob\u021binem un mesaj nou cu c\u00e2mpurile ini\u021bializate \u0219i un buffer de date gol. Pentru a ad\u0103uga date \u00een mesaj, trebuie s\u0103 le copiem \u00een buffer-ul blocului:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">memcpy(msg-&gt;b_rptr, data, size);<\/code><\/pre>\n<p><\/p>\n<p>unde <em>data<\/em> \u2014 un pointer c\u0103tre sursa de date, iar <em>dimensiune<\/em> \u2014 dimensiunea acestora.<br \/>\nApoi trebuie actualizat pointer-ul pe punctul de scriere, astfel \u00eenc\u00e2t s\u0103 indice din nou \u00eenceputul zonei libere din buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">msg-&gt;b_wptr = msg-&gt;b_wptr + size<\/code><\/pre>\n<p><\/p>\n<p>Dac\u0103 este necesar s\u0103 se creeze un mesaj dintr-un buffer existent, f\u0103r\u0103 copiere, atunci se utilizeaz\u0103 func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); <\/code><\/pre>\n<p><\/p>\n<p>Func\u021bia, dup\u0103 crearea mesajului \u0219i a structurii blocului de date, va configura indicatorii s\u0103i c\u0103tre date la adresa <em>buf<\/em>. Adic\u0103, \u00een acest caz, buffer-ul de date nu se afl\u0103 imediat dup\u0103 c\u00e2mpurile antetului blocului de date, a\u0219a cum a fost la crearea blocului de date prin func\u021bie. <em>datab_alloc()<\/em>Bufferul de date transmis func\u021biei va r\u0103m\u00e2ne acolo unde a fost, dar cu ajutorul pointerilor va fi legat de antetul recent creat al blocului de date, iar acesta respectiv la mesaj.<\/p>\n<p><\/p>\n<p>Pentru un mesaj <em>mblk_t<\/em> pot fi ata\u0219ate secven\u021bial mai multe blocuri de date. Acest lucru se face prin func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); <\/code><\/pre>\n<p><\/p>\n<p><em>mp<\/em> \u2014 mesajul c\u0103ruia i se va ad\u0103uga un alt bloc de date;<br \/>\n<em>data<\/em> \u2014 pointerul la bloc, o copie a c\u0103ruia va fi ad\u0103ugat\u0103 la mesaj;<br \/>\n<em>dimensiune<\/em> \u2014 dimensiunea datelor;<br \/>\n<em>pad<\/em> \u2014 flagul c\u0103 dimensiunea memoriei alocate trebuie s\u0103 fie aliniat\u0103 la limita de 4 octe\u021bi (completarea se va face cu zero-uri). <\/p>\n<p><\/p>\n<p>Dac\u0103 \u00een bufferul existent al mesajului exist\u0103 suficient spa\u021biu, atunci noile date vor fi lipite la datele deja existente. Dac\u0103 spa\u021biul liber \u00een bufferul de date al mesajului este mai mic dec\u00e2t <em>dimensiune<\/em>, atunci se creeaz\u0103 un mesaj nou, cu o dimensiune suficient\u0103 a bufferului, iar datele sunt copiate \u00een bufferul s\u0103u. Acest mesaj nou este ata\u0219at la cel original cu ajutorul pointerului <em>b_cont<\/em>. \u00cen acest caz, mesajul devine un tuplu. <\/p>\n<p><\/p>\n<p>Dac\u0103 \u00een tuplu trebuie ad\u0103ugat un alt bloc de date, atunci trebuie folosit\u0103 func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);<\/code><\/pre>\n<p><\/p>\n<p>aceasta va g\u0103si ultimul mesaj din tuplu (acesta va fi <em>b_cont<\/em> va fi nul) \u0219i va apela func\u021bia <em>appendb()<\/em>.<\/p>\n<p><\/p>\n<p>Pentru a afla dimensiunea datelor din mesaj sau din tuplu, se poate folosi func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>acesta va parcurge toate mesajele din tuplu \u0219i va returna cantitatea total\u0103 de date din bufferii de date ai acestor mesaje. Pentru fiecare mesaj, cantitatea de date este calculat\u0103 astfel:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\"> mp-&gt;b_wptr - mp-&gt;b_rptr<\/code><\/pre>\n<p><\/p>\n<p>Pentru a concatena dou\u0103 tuple se folose\u0219te func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *concatb(mblk_t *mp, mblk_t *newm);<\/code><\/pre>\n<p><\/p>\n<p>aceasta va ata\u0219a tuplul <em>newm<\/em> la coada tuplului <em>mp<\/em> \u0219i va returna pointerul la ultimul mesaj al tuplului rezultat.<\/p>\n<p><\/p>\n<p>Dac\u0103 este necesar, tuplul poate fi transformat \u00eentr-un singur mesaj cu un bloc unic de date, acest lucru se face cu func\u021bia:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,int len);<\/code><\/pre>\n<p><\/p>\n<p>dac\u0103 argumentul <em>len<\/em> este egal cu -1, atunci dimensiunea bufferului alocat este determinat\u0103 automat. Dac\u0103 <em>len<\/em> dac\u0103 este un num\u0103r pozitiv, atunci va fi creat un buffer de aceast\u0103 dimensiune \u0219i \u00een el vor fi copiate datele mesajelor tuple. Dac\u0103 bufferul se umple, copierea se va opri. Primul mesaj al tuplei va primi un buffer de o nou\u0103 dimensiune cu datele copiate. Celelalte mesaje vor fi \u0219terse, iar memoria va fi returnat\u0103 \u00een heap.<\/p>\n<p><\/p>\n<p>La \u0219tergerea structurii <em>mblk_t<\/em> se ia \u00een considerare num\u0103rul de referin\u021be al blocului de date, dac\u0103 la apelul <em>freeb()<\/em> ajunge la zero, atunci bufferul de date este \u0219ters \u00eempreun\u0103 cu instan\u021ba <em>mblk_t<\/em>, la care se refer\u0103.<\/p>\n<p><\/p>\n<p>Ini\u021bializarea c\u00e2mpurilor unui nou mesaj:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void mblk_init(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Ad\u0103ugarea unei noi por\u021bii de date \u00een mesaj: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);<\/code><\/pre>\n<p><\/p>\n<p>Dac\u0103 noile date nu \u00eencap \u00een spa\u021biul liber al bufferului de date al mesajului, atunci la mesaj se ata\u0219eaz\u0103 un mesaj creat separat cu bufferul de dimensiunea necesar\u0103 (\u00een primul mesaj se stabile\u0219te un pointer c\u0103tre mesajul ad\u0103ugat), mesajul se transform\u0103 \u00eentr-o tupl\u0103.<\/p>\n<p><\/p>\n<p>Ad\u0103ugarea unei por\u021bii de date \u00een tupl\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); <\/code><\/pre>\n<p><\/p>\n<p>Func\u021bia apeleaz\u0103 appendb() \u00eentr-un ciclu.<\/p>\n<p><\/p>\n<p>Combinarea a dou\u0103 tuple \u00eentr-una:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *concatb(mblk_t *mp, mblk_t *newm);<\/code><\/pre>\n<p><\/p>\n<p>Mesaj <em>newm<\/em> va fi ata\u0219at la <em>mp<\/em>.<\/p>\n<p><\/p>\n<p>Crearea unei copii a unui mesaj unic:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copyb(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Copierea complet\u0103 a tuplei cu toate blocurile de date:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copymsg(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Elementele tuplei sunt copiate \u00een acest fel de func\u021bia <em>copyb()<\/em>.<\/p>\n<p><\/p>\n<p>Crearea unei copii u\u0219oare a mesajului. <em>mblk_t<\/em>\u00cen acest caz, blocul de date nu este copiat, ci se m\u0103re\u0219te num\u0103rul s\u0103u de referin\u021be. <em>db_ref<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *dupb(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Crearea unei copii u\u0219oare a tuplei. Blocurile de date nu sunt copiate, doar se m\u0103resc numerele lor de referin\u021be. <em>db_ref<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *dupmsg(mblk_t* m);<\/code><\/pre>\n<p><\/p>\n<p>Concatenarea tuturor mesajelor din tupl\u0103 \u00eentr-un singur mesaj:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,size_t len);<\/code><\/pre>\n<p><\/p>\n<p>Dac\u0103 argumentul <em>len<\/em> este -1, atunci dimensiunea bufferului rezervat este determinat\u0103 automat.<\/p>\n<p><\/p>\n<p>\u0218tergerea mesajului, tuplei:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void freemsg(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Num\u0103rul de referin\u021be al blocului de date este redus cu o unitate. Dac\u0103 ajunge la zero, atunci blocul de date este, de asemenea, \u0219ters.<\/p>\n<p><\/p>\n<p>Calcularea volumului total de date din mesaj sau tupl\u0103.<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">size_t msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Extra\u0219ionarea mesajului din coada din spate:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *ms_queue_peek_last (q);<\/code><\/pre>\n<p><\/p>\n<p>Copierea con\u021binutului c\u00e2mpurilor rezervate ale unui mesaj \u00een alt mesaj (de fapt, \u00een aceste c\u00e2mpuri se afl\u0103 flag-uri care sunt utilizate de media streamer):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_meta_copy(const mblk_t *source, mblk *dest);<\/code><\/pre>\n<p><\/p>\n<h3 id=\"ochered-queue_t\">Coad\u0103 <em>queue_t<\/em><\/h3>\n<p><\/p>\n<p>Coada de mesaje \u00een mediastreamer este implementat\u0103 ca o list\u0103 dublu \u00eenl\u0103n\u021buit\u0103 circulat\u0103. Fiecare element al listei con\u021bine un pointer c\u0103tre un bloc de date cu m\u0103sur\u0103tori ale semnalului. Astfel, se deplaseaz\u0103 doar pointerii c\u0103treblocuri de date, \u00een timp ce datele \u00een sine r\u0103m\u00e2n immobile. Cu alte cuvinte, se mi\u0219c\u0103 doar referin\u021bele la acestea.<br \/>\nStructura care descrie coada <em>queue_t<\/em>, este prezentat\u0103 mai jos:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _queue\n{\n   mblk_t _q_stopper; \/* \"Elementul gol\" al cozii, nu indic\u0103 la date, folosit doar pentru gestionarea cozii. La ini\u021bializarea cozii (qinit()), indicatorii s\u0103i sunt seta\u021bi astfel \u00eenc\u00e2t s\u0103 indice chiar pe el. *\/\n   int q_mcount;        \/\/ Num\u0103rul de elemente din coad\u0103.\n} queue_t;<\/code><\/pre>\n<p><\/p>\n<p>Structura con\u021bine un c\u00e2mp - pointer <em>_q_stopper<\/em> de tip *mblk_t, care indic\u0103 asupra primului element (mesaj) din coad\u0103. Al doilea c\u00e2mp al structurii este un contor al mesajelor aflate \u00een coad\u0103.<br \/>\n\u00cen figura de mai jos este prezentat\u0103 coada cu numele q1, care con\u021bine 4 mesaje m1, m2, m3, m4.<br \/>\n<img decoding=\"async\" alt=\"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11\" src=\"\/wp-content\/uploads\/2020\/04\/07fc043861edf15e5eb40e49c8ec5e3e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n\u00cen figura urm\u0103toare este prezentat\u0103 coada cu numele q1, care con\u021bine 4 mesaje m1, m2, m3, m4. Mesajul m2 este capul tuplului, \u00een care se afl\u0103 \u0219i dou\u0103 alte mesaje m2_1 \u0219i m2_2.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Examin\u0103m motorul VoIP Mediastreamer2. Partea 11\" src=\"\/wp-content\/uploads\/2020\/04\/8fa7667f3690674a2ee321b4a6c39e1d.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-ocheredyami-queue_t\">Func\u021bii pentru manipularea cozilor queue_t<\/h3>\n<p><\/p>\n<p>Ini\u021bializarea cozii:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void qinit(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>C\u00e2mp <em>_q_stopper<\/em> (\u00een continuare \u00eel vom numi \"stopper\") este ini\u021bializat de o func\u021bie <em>mblk_init()<\/em>, pointerul s\u0103u c\u0103tre elementul anterior \u0219i cel urm\u0103tor sunt seta\u021bi astfel \u00eenc\u00e2t s\u0103 se indic\u0103 asupra lui \u00eensu\u0219i. Contorul elementelor din coad\u0103 este resetat.<\/p>\n<p><\/p>\n<p>Ad\u0103ugarea unui nou element (mesaj):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void putq(queue_t *q, mblk_t *m);<\/code><\/pre>\n<p><\/p>\n<p>Un nou element <em>m<\/em> este ad\u0103ugat la sf\u00e2r\u0219itul listei, pointerii elementului sunt seta\u021bi astfel \u00eenc\u00e2t stop s\u0103 devin\u0103 pentru el urm\u0103torul element, iar el pentru stop anteriorul. Contorul elementelor cozii este incrementat.<\/p>\n<p><\/p>\n<p>Extrac\u021bia unui element din coad\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * getq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>se extrage acel mesaj care se afl\u0103 dup\u0103 stop, contorul elementelor este decrementat. Dac\u0103 \u00een coad\u0103, \u00een afar\u0103 de stop, nu mai sunt alte elemente, se returneaz\u0103 0.<\/p>\n<p><\/p>\n<p>Inserarea unui mesaj \u00een coad\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void insq(queue_t *q, mblk_t *emp, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Elementul <em>mp<\/em> se insereaz\u0103 \u00eenainte de elementul <em>emp<\/em>. Dac\u0103 <em>emp<\/em>=0, astfel mesajul este ad\u0103ugat la coada cozii.<\/p>\n<p><\/p>\n<p>Extrac\u021bia mesajului din capul cozii:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void remq(queue_t *q, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Contorul elementelor este decrementat.<\/p>\n<p><\/p>\n<p>Citirea pointerului c\u0103tre primul element din coad\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * peekq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>\u0218tergerea tuturor elementelor din coad\u0103 cu \u0219tergerea elementelor \u00een sine:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void flushq(queue_t *q, int how);<\/code><\/pre>\n<p><\/p>\n<p>Argumentul <em>how<\/em> nu este utilizat. Contorul elementelor din coad\u0103 este setat pe zero.<\/p>\n<p><\/p>\n<p>Macro pentru citirea pointerului la ultimul element din coad\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * qlast(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>C\u00e2nd se lucreaz\u0103 cu cozile de mesaje, trebuie avut \u00een vedere c\u0103, la apelul <em>ms_queue_put(q, m)<\/em> cu un pointer nul la mesaj, func\u021bia intr\u0103 \u00eentr-o bucl\u0103. Programul dvs. va r\u0103m\u00e2ne blocat. Similar se comport\u0103 <em>ms_queue_next(q, m)<\/em>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-filtrov\">Conectarea filtrelor<\/h3>\n<p><\/p>\n<p>Coad\u0103 descris\u0103 mai sus este folosit\u0103 pentru a transmite mesaje de la un filtru la altul sau de la un filtru la mai multe filtre. filtrele \u0219i conexiunile dintre ele formeaz\u0103 un graf orientat. Intrarea sau ie\u0219irea unui filtru le vom numi generic \"pin\". Pentru a descrie ordinea conect\u0103rii filtrelor \u00eentre ele, \u00een mediastreamer este folosit conceptul de \"punct de semnalizare\". Punctul de semnalizare este o structur\u0103 <em>_MSCPoint<\/em>, care con\u021bine un pointer c\u0103tre filtru \u0219i num\u0103rul unuia dintre pinurile sale, de aceea descrie conexiunea uneia dintre intr\u0103rile sau ie\u0219irile filtrului. <\/p>\n<p><\/p>\n<h4 id=\"signalnaya-tochka-grafa-obrabotki-dannyh\">Punctul de semnalizare al graficului de procesare a datelor<\/h4>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSCPoint{\nstruct _MSFilter *filter; \nint pin;                        \n} MSCPoint;\n<\/code><\/pre>\n<p><\/p>\n<p>Pinurile filtrelor sunt numerotate \u00eencep\u00e2nd de la zero. <\/p>\n<p><\/p>\n<p>Conexiunea dintre dou\u0103 pinuri printr-o coad\u0103 de mesaje este descris\u0103 de structur\u0103 <em>_MSQueue<\/em>, care con\u021bine coada de mesaje \u0219i pointere c\u0103tre dou\u0103 puncte de semnal, pe care le conecteaz\u0103:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSQueue\n{\nqueue_t q;\nMSCPoint prev;\nMSCPoint next;\n}MSQueue;\n<\/code><\/pre>\n<p><\/p>\n<p>Vom numi aceast\u0103 structur\u0103 link de semnal. Fiecare filtru media streamer con\u021bine o tabel\u0103 de linkuri de intrare \u0219i o tabel\u0103 de linkuri de ie\u0219ire (<em>MSQueue<\/em>). Dimensiunea tabelelor este setat\u0103 la crearea filtrului, am f\u0103cut deja acest lucru cu ajutorul unei variabile exportate de tip <em>MSFilterDesc<\/em>, atunci c\u00e2nd am dezvoltat propriul nostru filtru. Mai jos este prezentat\u0103 structura care descrie orice filtru din media streamer, <em>MSFilter<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">\nstruct _MSFilter{\n    MSFilterDesc *desc;    \n    \/* Pointer to the filter descriptor. *\/\n    \/* Protected attributes, they cannot be moved or removed, otherwise functionality with plugins will be disrupted. *\/\n    ms_mutex_t lock;      \/* Semaphore. *\/\n    MSQueue **inputs;     \/* Input links table. *\/\n    MSQueue **outputs;    \/* Output links table. *\/\n    struct _MSFactory *factory; \/* Pointer to the factory that created this filter instance. *\/\n    void *padding;              \/* Not used, will be employed if protected fields are added. *\/\n    void *data;                 \/* Pointer to an arbitrary structure for storing the filter's internal state and intermediate calculations. *\/\n    struct _MSTicker *ticker;   \/* Pointer to the ticker object, which must not be null when the process() function is called. *\/\n    \/*private attributes, they can be moved and changed at any time*\/\n    MSList *notify_callbacks; \/* List of callbacks used for processing filter events. *\/\n    uint32_t last_tick;       \/* The number of the last tick when the process() call was executed. *\/\n    MSFilterStats *stats;     \/* Filter performance statistics.*\/\n    int postponed_task; \/*Number of postponed tasks. Some filters may delay data processing (process() call) for several ticks.*\/\n    bool_t seen;  \/* Flag used by the ticker to mark that this filter instance has already been serviced during this tick.*\/\n};\ntypedef struct _MSFilter MSFilter;\n<\/code><\/pre>\n<p><\/p>\n<p>Dup\u0103 ce am conectat filtrele \u00een programul C conform planului nostru (dar nu am conectat tickerul), astfel am creat un graf orientat, al c\u0103rui noduri sunt instan\u021bele structurilor <em>MSFilter<\/em>, iar muchiile sunt instan\u021bele leg\u0103turilor <em>MSQueue<\/em>. <\/p>\n<p><\/p>\n<h3 id=\"zakulisnaya-deyatelnost-tikera\">Activitate de culise a ticker-ului<\/h3>\n<p><\/p>\n<p>C\u00e2nd v-am spus c\u0103 tickerul este un filtru surs\u0103 de ritmuri, nu am spus \u00eentreaga adev\u0103r despre el. Tickerul este un obiect care declan\u0219eaz\u0103 func\u021biile la intervale regulate <em>process()<\/em> a tuturor filtrelor din schem\u0103 (graf) la care este conectat. C\u00e2nd \u00een programul C conect\u0103m un ticker la un filtru al graficului, ar\u0103t\u0103m ticker-ului graficul pe care acesta \u00eel va gestiona de la acel moment, p\u00e2n\u0103 c\u00e2nd \u00eel deconect\u0103m. Dup\u0103 conectare, ticker-ul \u00eencepe s\u0103 inspecteze graficul \u00eencredin\u021bat, \u00eentocmind o list\u0103 cu filtrele care \u00eel includ. Pentru a nu \"num\u0103ra\" acela\u0219i filtru de dou\u0103 ori, el marcheaz\u0103 filtrele descoperite, activ\u00e2nd un semnal \u00een acestea <em>seen<\/em>. C\u0103utarea se realizeaz\u0103 prin tabelele leg\u0103turilor, care exist\u0103 pe fiecare filtru. <\/p>\n<p><\/p>\n<p>\u00cen timpul turului s\u0103u de documentare, tickerul verific\u0103 dac\u0103 exist\u0103 printre filtre m\u0103car unul care joac\u0103 rolul unei surse de blocuri de date. Dac\u0103 nu se g\u0103sesc astfel de filtre, graficul este considerat incorect \u0219i tickerul se \u00eenchide \u00een mod abrupt.<\/p>\n<p><\/p>\n<p>Dac\u0103 graficul se dovede\u0219te a fi \"corect\", pentru fiecare filtru g\u0103sit, pentru ini\u021bializare, se apeleaz\u0103 o func\u021bie <em>preprocess()<\/em>. Atunci c\u00e2nd vine momentul pentru urm\u0103toarea etap\u0103 de procesare (de obicei la fiecare 10 milisecunde), tickerul apeleaz\u0103 func\u021bia <em>process()<\/em> pentru toate filtrele surs\u0103 g\u0103site anterior, \u0219i apoi pentru celelalte filtre din list\u0103. Dac\u0103 filtru are leg\u0103turi de intrare, atunci apelarea func\u021biei <em>process()<\/em> se repet\u0103 p\u00e2n\u0103 c\u00e2nd cozile de leg\u0103turi de intrare se golesc. Dup\u0103 aceea, trece la urm\u0103torul filtru din list\u0103 \u0219i \"rula\" acesta p\u00e2n\u0103 c\u00e2nd leg\u0103turile de intrare sunt eliberate de mesaje. Ticker-ul trece de la un filtru la altul p\u00e2n\u0103 c\u00e2nd lista se termin\u0103. Aici se \u00eencheie procesarea ciclului. <\/p>\n<p><\/p>\n<p>Acum ne vom \u00eentoarce la tupluri \u0219i vom discuta despre motivul pentru care o astfel de entitate a fost ad\u0103ugat\u0103 \u00een mediastreamer. \u00cen general, volumul de date necesar algoritmului care func\u021bioneaz\u0103 \u00een cadrul filtrului nu coincide \u0219i nu este un multiplu al dimensiunii bufferelor de date care intr\u0103. De exemplu, scriem un filtru care efectueaz\u0103 o transformare rapid\u0103 Fourier, care prin defini\u021bie poate procesa doar blocuri de date ale c\u0103ror dimensiuni sunt puteri ale lui 2. S\u0103 presupunem c\u0103 este vorba de 512 e\u0219antioane. Dac\u0103 datele sunt generate de un canal telefonic, atunci bufferul de date al fiec\u0103rui mesaj de intrare va aduce 160 e\u0219antioane de semnal. Exist\u0103 tenta\u021bia de a nu lua date de pe intrare p\u00e2n\u0103 nu avem cantitatea necesar\u0103 de date. Dar, \u00een acest caz, va ap\u0103rea o coliziune cu tickerul, care va \u00eencerca f\u0103r\u0103 succes s\u0103 r\u0103sfoiasc\u0103 filtrul p\u00e2n\u0103 c\u00e2nd linia de intrare este golit\u0103. Anterior am denumit aceast\u0103 regul\u0103 ca fiind al treilea principiu de func\u021bionare al filtrului. Conform acestui principiu, func\u021bia process() a filtrului trebuie s\u0103 preia toate datele din cozile de intrare.<\/p>\n<p><\/p>\n<p>\u00cen plus, nu se pot prelua doar 512 \u00eenregistr\u0103ri de la intrare, deoarece se pot lua doar blocuri \u00eentregi, adic\u0103 filtrul va trebui s\u0103 preia 640 de \u00eenregistr\u0103ri \u0219i, utiliz\u00e2nd 512 dintre ele, restul va r\u0103m\u00e2ne p\u00e2n\u0103 la acumularea unei noi por\u021bii de date. Astfel, filtrul nostru, pe l\u00e2ng\u0103 activitatea sa principal\u0103, trebuie s\u0103 asigure ac\u021biuni auxiliare pentru stocarea intermediar\u0103 a datelor de intrare. Dezvoltatorii mediastreamerului, pentru a rezolva aceast\u0103 problem\u0103 comun\u0103, au dezvoltat un obiect special \u2013 MSBufferizer (bufferizer), care rezolv\u0103 aceast\u0103 problem\u0103 prin intermediul tuplurilor. <\/p>\n<p><\/p>\n<h3 id=\"buferizator-msbufferizer\">Buffer (MSBufferizer)<\/h3>\n<p><\/p>\n<p>Acesta este un obiect care va acumula datele de intrare \u00een cadrul filtrului \u0219i le va \u00eencepe s\u0103 le furnizeze pentru procesare, odat\u0103 ce cantitatea de informa\u021bii devine suficient\u0103 pentru a rula algoritmul filtrului. \u00cen timp ce bufferizerul acumuleaz\u0103 date, filtrul va func\u021biona \u00een modul gol, f\u0103r\u0103 a consuma puterea de calcul a procesorului. Dar, odat\u0103 ce func\u021bia de citire din bufferizer returneaz\u0103 o valoare diferit\u0103 de zero, func\u021bia process() a filtrului \u00eencepe s\u0103 preia \u0219i s\u0103 proceseze datele din bufferizer \u00een por\u021bii de dimensiunea necesar\u0103, p\u00e2n\u0103 la epuizarea acestora.<br \/>\nDatele care nu sunt utilizate r\u0103m\u00e2n \u00een bufferizer ca prim element al tuplului, la care se ata\u0219eaz\u0103 blocurile ulterioare de date de intrare. <\/p>\n<p><\/p>\n<p>Structura care descrie bufferizerul: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">struct _MSBufferizer{\nqueue_t q; \/* Coada mesajelor. *\/\nint size; \/* Dimensiunea total\u0103 a datelor aflate \u00een bufferizer \u00een acest moment. *\/\n};\ntypedef struct _MSBufferizer MSBufferizer;<\/code><\/pre>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-msbufferizer\">Func\u021bii pentru manipularea MSBufferizer<\/h3>\n<p><\/p>\n<p>Crearea unei noi instan\u021be a bufferizerului:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">MSBufferizer * ms_bufferizer_new(void);<\/code><\/pre>\n<p><\/p>\n<p>Se aloc\u0103 memorie, se ini\u021bializeaz\u0103 \u00een <em>ms_bufferizer_init()<\/em> \u0219i se returneaz\u0103 un pointer.<\/p>\n<p><\/p>\n<p>Func\u021bia de ini\u021bializare:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_init(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Se ini\u021biaz\u0103 coada <em>q<\/em>, c\u00e2mp <em>dimensiune<\/em> se seteaz\u0103 la zero.<\/p>\n<p><\/p>\n<p>Ad\u0103ugarea unui mesaj:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); <\/code><\/pre>\n<p><\/p>\n<p>Mesajul m este ad\u0103ugat \u00een coad\u0103. Dimensiunea calculat\u0103 a blocurilor de date este ad\u0103ugat\u0103 la <em>dimensiune<\/em>.<\/p>\n<p><\/p>\n<p>Traducea \u00een bufferizer a tuturor mesajelor din coada de date a link-ului <em>q<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   <\/code><\/pre>\n<p><\/p>\n<p>Transferul mesajelor din link <em>q<\/em> \u00een bufferizer se efectueaz\u0103 cu ajutorul func\u021biei <em>ms_bufferizer_put()<\/em>.<\/p>\n<p><\/p>\n<p>Citirea din bufferizer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); <\/code><\/pre>\n<p><\/p>\n<p>Dac\u0103 dimensiunea datelor acumulate \u00een bufferizer este mai mic\u0103 dec\u00e2t cerut\u0103 (<em>datalen<\/em>), func\u021bia returneaz\u0103 zero, copierea datelor \u00een data nu se efectueaz\u0103. \u00cen caz contrar, se efectueaz\u0103 copierea secven\u021bial\u0103 a datelor din tuplurile aflate \u00een buffer. Dup\u0103 copiere, tuplul este \u0219ters \u0219i memoria eliberat\u0103. Copierea se \u00eencheie \u00een momentul \u00een care sunt copiate datalen octe\u021bi. Dac\u0103 spa\u021biul se termin\u0103 \u00een mijlocul unui bloc de date, atunci \u00een acest mesaj, blocul de date va fi redus la partea r\u0103mas\u0103 necopiat\u0103. La urm\u0103toarea apelare, copierea va continua de la acest loc.<\/p>\n<p><\/p>\n<p>Citirea num\u0103rului de date disponibile \u00een acest moment \u00een buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_get_avail(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Returneaz\u0103 c\u00e2mpul <em>dimensiune<\/em> buffer-ului.<\/p>\n<p><\/p>\n<p>Scoaterea unei p\u0103r\u021bi din datele aflate \u00een buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);<\/code><\/pre>\n<p><\/p>\n<p>Num\u0103rul specificat de octe\u021bi de date este extras \u0219i ignorat. Cele mai vechi date sunt eliminate.<\/p>\n<p><\/p>\n<p>\u0218tergerea tuturor mesajelor aflate \u00een buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_flush(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Contorul de date este resetat la zero.<\/p>\n<p><\/p>\n<p>\u0218tergerea tuturor mesajelor aflate \u00een buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_uninit(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Resetarea contorului nu se efectueaz\u0103.<\/p>\n<p><\/p>\n<p>\u0218tergerea buffer-ului \u0219i eliberarea memoriei:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_destroy(MSBufferizer *obj);  <\/code><\/pre>\n<p><\/p>\n<p>Exemple de utilizare a buffer-ului pot fi g\u0103site \u00een codul surs\u0103 al mai multor filtre de medistreamer. De exemplu, \u00een filtrul MS_L16_ENC, care efectueaz\u0103 permutarea octe\u021bilor \u00een e\u0219antioane din ordinea re\u021belei \u00een ordinea gazd\u0103:<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/BelledonneCommunications\/mediastreamer2\/blob\/1a9e8dec369a65ed10db975f5b77b92dc999b096\/src\/audiofilters\/l16.c\"> l16.c<\/a><\/noindex><\/p>\n<p><\/p>\n<p>\u00cen articolul urm\u0103tor, vom examina problema evalu\u0103rii sarcinii pe ticker \u0219i metodele de combatere a supra\u00eenc\u0103rc\u0103rii computa\u021bionale \u00een mediastreamer.<\/p>\n<p>Sursa: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/499010\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0411\u043b\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 dblk_t \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 mblk_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 mblk_t \u041e\u0447\u0435\u0440\u0435\u0434\u044c queue_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 queue_t \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0421\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0433\u0440\u0430\u0444\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0417\u0430\u043a\u0443\u043b\u0438\u0441\u043d\u0430\u044f \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0438\u043a\u0435\u0440\u0430 \u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0442\u043e\u0440 (MSBufferizer) \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MSBufferizer \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043c\u044b \u043f\u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79541,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79540","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0437\u0443\u0447\u0430\u0435\u043c VoIP-\u0434\u0432\u0438\u0436\u043e\u043a Mediastreamer2. \u0427\u0430\u0441\u0442\u044c 11 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-04-28T05:42:01+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-04-28T05:42:01+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47\u00cenv\u0103\u021b\u0103m motorul VoIP Mediastreamer2. Partea 11 | ProHoster","description":"Materialul articolului este preluat de pe canalul meu de Zen.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0437\u0443\u0447\u0430\u0435\u043c VoIP-\u0434\u0432\u0438\u0436\u043e\u043a Mediastreamer2. \u0427\u0430\u0441\u0442\u044c 11 | ProHoster","og:description":"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430.","og:url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-04-28T05:42:01+00:00","article:modified_time":"2020-04-28T05:42:01+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79540","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 12:40:33","updated":"2022-10-06 16:26:59","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/79540","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=79540"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/79540\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/79541"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=79540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=79540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=79540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}