{"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\/it\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","title":{"rendered":"Studiamo il motore VoIP Mediastreamer2. Parte 11","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Il materiale dell'articolo \u00e8 tratto dal mio <noindex><a rel=\"nofollow\" href=\"https:\/\/zen.yandex.ru\/profile\/editor\/id\/5e3f8e0751f5346faab4fc7a\">canale Zen<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Studiamo il motore VoIP Mediastreamer2. Parte 11\" src=\"\/wp-content\/uploads\/2020\/04\/b66fab43eb45c626c50f0383dcfa7cca.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Meccanismo di trasferimento dei dati<\/p>\n<p><\/p>\n<ul>\n<li>Blocco dati dblk_t<\/li>\n<li>Messaggio mblk_t<\/li>\n<li>Funzioni per la gestione dei messaggi mblk_t<\/li>\n<li>Coda queue_t<\/li>\n<li>Funzioni per la gestione delle code queue_t<\/li>\n<li>Collegamento dei filtri<\/li>\n<li>Punto di segnale del grafo di elaborazione dei dati<\/li>\n<li>Attivit\u00e0 dietro le quinte del ticker<\/li>\n<li>Bufferizzatore (MSBufferizer)<\/li>\n<li>Funzioni per la gestione di MSBufferizer<\/li>\n<\/ul>\n<p><\/p>\n<p>Nel passato <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/497932\/\">articolo<\/a><\/noindex> Abbiamo sviluppato il nostro filtro. Questo articolo \u00e8 dedicato alla struttura del meccanismo interno di trasferimento dei dati tra i filtri del media streamer. Ci\u00f2 permetter\u00e0 in futuro di scrivere filtri sofisticati con minori sforzi.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"mehanizm-peremescheniya-dannyh\">Meccanismo di trasferimento dei dati<\/h2>\n<p><\/p>\n<p>Il trasferimento dei dati nel media streamer avviene tramite le code descritte dalla struttura <em>queue_t<\/em>. Attraverso le code si muovono le sequenze di messaggi di tipo <em>mblk_t<\/em>, che di per s\u00e9 non contengono dati del segnale, ma solo collegamenti al messaggio precedente, successivo e al blocco dati. In aggiunta, vorrei sottolineare che c'\u00e8 anche un campo per collegare un messaggio dello stesso tipo, il che permette di organizzare una lista concatenata di messaggi. Gruppo di messaggi uniti da tale lista sar\u00e0 chiamato tupla. Pertanto, qualsiasi elemento della coda pu\u00f2 essere un singolo messaggio. <em>mblk_t<\/em>, o forse la testa del gruppo di messaggi <em>mblk_t<\/em>. Ogni messaggio del gruppo pu\u00f2 avere il proprio blocco di dati associato. Discuteremo perch\u00e9 sono necessari i gruppi di messaggi un po' pi\u00f9 tardi.<\/p>\n<p><\/p>\n<p>Come gi\u00e0 detto, il messaggio da solo non contiene un blocco di dati, ma soltanto un puntatore all'area di memoria dove \u00e8 memorizzato il blocco. In questa parte, l'immagine generale del funzionamento dello streamer media ricorda il magazzino delle porte nel cartone animato \"Monsters, Inc.\", dove le porte (collegamenti ai dati \u2014 stanze) si muovono a una velocit\u00e0 folle lungo nastri trasportatori, mentre le stanze stesse rimangono immobili. <\/p>\n<p><\/p>\n<p>Ora, procedendo attraverso la gerarchia dal basso verso l'alto, esaminiamo in dettaglio le entit\u00e0 del meccanismo di trasmissione dei dati nel media streamer.<\/p>\n<p><\/p>\n<h3 id=\"blok--dannyh-dblk_t\">Blocco di dati <em>dblk_t<\/em><\/h3>\n<p><\/p>\n<p>Un blocco di dati \u00e8 composto da un'intestazione e un buffer di dati. L'intestazione \u00e8 descritta dalla seguente struttura,<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct datab\n{\nunsigned char *db_base; \/\/ Puntatore all'inizio del buffer di dati.\nunsigned char *db_lim;  \/\/ Puntatore alla fine del buffer di dati.\nvoid (*db_freefn)(void*); \/\/ Funzione di liberazione della memoria quando si elimina il blocco.\nint db_ref; \/\/ Contatore dei riferimenti.\n} dblk_t;<\/code><\/pre>\n<p><\/p>\n<p>I campi della struttura contengono puntatori all'inizio del buffer, alla fine del buffer e a una funzione per eliminare il buffer dati. L'ultimo elemento nell'intestazione <em>db_ref<\/em> \u2014 \u00e8 un contatore dei riferimenti; quando raggiunge zero, funge da segnale per la rimozione di questo blocco dalla memoria. Se il blocco dati \u00e8 stato creato con la funzione <em>datab_alloc() <\/em>, il buffer dati sar\u00e0 allocato in memoria immediatamente dopo l'intestazione. In tutti gli altri casi, il buffer pu\u00f2 trovarsi separatamente. Nel buffer dati si troveranno le letture del segnale o altri dati che vogliamo elaborare con i filtri.<\/p>\n<p><\/p>\n<p>Una nuova istanza di un blocco dati viene creata con la funzione:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">dblk_t *datab_alloc(int size);<\/code><\/pre>\n<p><\/p>\n<p>Il parametro di input \u00e8 la dimensione dei dati che il blocco dovr\u00e0 contenere. Viene allocata pi\u00f9 memoria per collocare all'inizio della memoria allocata l'intestazione \u2014 la struttura <em>datab<\/em>. Tuttavia, quando vengono utilizzate altre funzioni, ci\u00f2 non avviene sempre; in alcuni casi, il buffer dati pu\u00f2 trovarsi separato dall'intestazione del blocco dati. I campi della struttura, al momento della creazione, sono configurati in modo tale che il suo campo <em>db_base<\/em> punte alla fine dell'area dati, mentre <em>db_lim<\/em> punta alla sua fine. Contatore dei riferimenti <em>db_ref<\/em> viene impostato a zero. Il puntatore della funzione di pulizia dei dati viene impostato a zero.<\/p>\n<p><\/p>\n<h3 id=\"soobschenie-mblk_t\">Messaggio <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Come gi\u00e0 detto, gli elementi della coda sono di tipo <em>mblk_t,<\/em> \u00e8 definito come segue:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct msgb\n{\n  struct msgb *b_prev;   \/\/ Puntatore all'elemento precedente della lista.\n  struct msgb *b_next;   \/\/ Puntatore all'elemento successivo della lista.\n  struct msgb *b_cont;   \/\/ Puntatore per collegare altri messaggi a un messaggio, per creare una tupla di messaggi.\n  struct datab *b_datap; \/\/ Puntatore alla struttura del blocco dati.\n  unsigned char *b_rptr; \/\/ Puntatore all'inizio dell'area dati per leggere i dati del buffer b_datap.\n  unsigned char *b_wptr; \/\/ Puntatore all'inizio dell'area dati per scrivere i dati del buffer b_datap.\n  uint32_t reserved1;    \/\/ Campo riservato 1, il media streamer vi inserisce informazioni di servizio.\n  uint32_t reserved2;    \/\/ Campo riservato 2, il media streamer vi inserisce informazioni di servizio.\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>Struttura <em>mblk_t<\/em> inizialmente contiene puntatori <em>b_prev<\/em>, <em>b_next<\/em>, necessari per organizzare una lista doppiamente collegata (che \u00e8 la coda <em>queue_t<\/em>). <\/p>\n<p><\/p>\n<p>Poi c'\u00e8 il puntatore <em>b_cont<\/em>, che viene utilizzato solo quando il messaggio entra nella tupla. Per l'ultimo messaggio nella tupla, questo puntatore rimane nullo. <\/p>\n<p><\/p>\n<p>Successivamente vediamo un puntatore al blocco dati <em>b_datap<\/em>, per il quale esiste il messaggio. Dopo di esso ci sono puntatori all'area all'interno del buffer dati del blocco. Il campo <em>b_rptr<\/em> indica il luogo da cui verranno letti i dati dal buffer. Il campo <em>b_wptr<\/em> indica il luogo da cui verr\u00e0 eseguita la scrittura nel buffer. <\/p>\n<p><\/p>\n<p>I campi rimanenti hanno carattere consultivo e non riguardano il funzionamento del meccanismo di trasferimento dati.<\/p>\n<p><\/p>\n<p>Di seguito \u00e8 mostrato un singolo messaggio con il nome <em>m1<\/em> e il blocco dati <em>d1<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Studiamo il motore VoIP Mediastreamer2. Parte 11\" src=\"\/wp-content\/uploads\/2020\/04\/e6a726b13491832258c1228f03bde802.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNella figura successiva \u00e8 raffigurata una tupla di tre messaggi <em>m1<\/em>, <em>m1_1<\/em>, <em>m1_2<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Studiamo il motore VoIP Mediastreamer2. Parte 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\">Le funzioni che lavorano con i messaggi <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Un nuovo messaggio <em>mblk_t<\/em> viene creato dalla funzione:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *allocb(int size, int pri); <\/code><\/pre>\n<p><\/p>\n<p>essa alloca in memoria un nuovo messaggio <em>mblk_t<\/em> con un blocco dati di dimensione specificata <em>dimensione<\/em>, il secondo argomento \u2014 <em>pri<\/em> non \u00e8 utilizzato nella versione della libreria considerata. Deve rimanere nullo. Durante l'esecuzione della funzione verr\u00e0 allocata memoria per la struttura del nuovo messaggio e verr\u00e0 chiamata la funzione <em>mblk_init()<\/em>, che azzerer\u00e0 tutti i campi dell'istanza creata della struttura e poi, utilizzando quanto detto sopra, <em>datab_alloc()<\/em>, creer\u00e0 un buffer di dati. Dopo di che verr\u00e0 eseguita la configurazione dei campi nella struttura: <\/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>In uscita otteniamo un nuovo messaggio con i campi inizializzati e un buffer di dati vuoto. Per aggiungere dati al messaggio, \u00e8 necessario copiarli nel buffer del blocco dati:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">memcpy(msg-&gt;b_rptr, data, size);<\/code><\/pre>\n<p><\/p>\n<p>dove <em>data<\/em> \u2014 puntatore alla fonte dei dati, e <em>dimensione<\/em> \u2014 la loro dimensione.<br \/>\nDopodich\u00e9 \u00e8 necessario aggiornare il puntatore alla posizione di scrittura, affinch\u00e9 punti di nuovo all'inizio dell'area libera nel 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>Se \u00e8 necessario creare un messaggio da un buffer esistente, senza copia, si utilizza la funzione:<\/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>La funzione, dopo aver creato il messaggio e la struttura del blocco dati, imposter\u00e0 i puntatori ai dati all'indirizzo <em>buf<\/em>. Cio\u00e8, in questo caso il buffer dati non si trova immediatamente dopo i campi dell'intestazione del blocco dati, come avveniva durante la creazione del blocco dati tramite la funzione. <em>datab_alloc()<\/em>. Il buffer di dati passato alla funzione rester\u00e0 dov'\u00e8, ma con l'uso dei puntatori verr\u00e0 collegato all'intestazione del blocco di dati appena creato, che a sua volta sar\u00e0 collegato al messaggio.<\/p>\n<p><\/p>\n<p>A un messaggio <em>mblk_t<\/em> possono essere concatenati pi\u00f9 blocchi di dati consecutivamente. Questo viene fatto dalla funzione:<\/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> \u00e8 il messaggio a cui verr\u00e0 aggiunto un altro blocco di dati;<br \/>\n<em>data<\/em> \u00e8 il puntatore al blocco, la cui copia sar\u00e0 aggiunta al messaggio;<br \/>\n<em>dimensione<\/em> \u00e8 la dimensione dei dati;<br \/>\n<em>pad<\/em> \u00e8 il flag che indica che la dimensione della memoria allocata deve essere allineata a un confine di 4 byte (l'integrazione sar\u00e0 eseguita con zeri). <\/p>\n<p><\/p>\n<p>Se nel buffer di dati del messaggio c'\u00e8 spazio sufficiente, i nuovi dati verranno incollati a quelli gi\u00e0 presenti. Se lo spazio nel buffer di dati del messaggio \u00e8 inferiore a <em>dimensione<\/em>, verr\u00e0 creato un nuovo messaggio con una dimensione del buffer adeguata e i dati saranno copiati nel suo buffer. Questo nuovo messaggio verr\u00e0 collegato a quello originale tramite il puntatore <em>b_cont<\/em>. In questo caso, il messaggio si trasforma in una tupla. <\/p>\n<p><\/p>\n<p>Se si desidera aggiungere un altro blocco di dati alla tupla, \u00e8 necessario utilizzare la funzione:<\/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>trover\u00e0 l'ultimo messaggio nella tupla (sar\u00e0 <em>b_cont<\/em> nullo) e chiamer\u00e0 la funzione per quel messaggio <em>appendb()<\/em>.<\/p>\n<p><\/p>\n<p>Puoi scoprire la dimensione dei dati nel messaggio o nella tupla tramite la funzione:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>scorrer\u00e0 tutti i messaggi della tupla e restituir\u00e0 il numero totale di dati nei buffer dati di questi messaggi. Per ogni messaggio, la quantit\u00e0 di dati viene calcolata in questo modo:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\"> mp-&gt;b_wptr - mp-&gt;b_rptr<\/code><\/pre>\n<p><\/p>\n<p>Per unire due tuple si utilizza la funzione:<\/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>aggiunge la tupla <em>newm<\/em> alla fine della tupla <em>mp<\/em> e restituisce un puntatore all'ultimo messaggio della tupla risultante.<\/p>\n<p><\/p>\n<p>Se necessario, la tupla pu\u00f2 essere convertita in un singolo messaggio con un blocco di dati unico, questo si fa con la funzione:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,int len);<\/code><\/pre>\n<p><\/p>\n<p>se l'argomento <em>len<\/em> \u00e8 uguale a -1, allora la dimensione del buffer allocato viene determinata automaticamente. Se <em>len<\/em> se \u00e8 un numero positivo, verr\u00e0 creato un buffer di questa dimensione e i dati dei messaggi della tupla verranno copiati in esso. Se il buffer si esaurisce, la copia si interromper\u00e0. Il primo messaggio della tupla ricever\u00e0 un buffer di nuova dimensione con i dati copiati. I messaggi rimanenti verranno eliminati e la memoria restituita al heap.<\/p>\n<p><\/p>\n<p>Durante l'eliminazione della struttura <em>mblk_t<\/em> si considera il contatore dei riferimenti del blocco dati, se alla chiamata <em>freeb()<\/em> risulta zero, il buffer dati viene eliminato insieme all'istanza <em>mblk_t<\/em>, alla quale fa riferimento.<\/p>\n<p><\/p>\n<p>Inizializzazione dei campi del nuovo messaggio:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void mblk_init(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Aggiunta a un messaggio di un ulteriore insieme di dati: <\/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>Se i nuovi dati non si adattano nello spazio libero del buffer dati del messaggio, un messaggio creato separatamente con un buffer della dimensione necessaria sar\u00e0 allegato al messaggio (nel primo messaggio verr\u00e0 impostato un puntatore verso il messaggio aggiunto), trasformando cos\u00ec il messaggio in una tupla.<\/p>\n<p><\/p>\n<p>Aggiunta di un insieme di dati alla tupla:<\/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>La funzione chiama appendb() in un ciclo.<\/p>\n<p><\/p>\n<p>Unione di due tuple in una sola:<\/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>Messaggio <em>newm<\/em> verr\u00e0 allegato a <em>mp<\/em>.<\/p>\n<p><\/p>\n<p>Creazione di una copia di un singolo messaggio:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copyb(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Copia completa della tupla con tutti i blocchi dati:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copymsg(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Gli elementi della tupla vengono copiati dalla funzione <em>copyb()<\/em>.<\/p>\n<p><\/p>\n<p>Creazione di una copia leggera del messaggio <em>mblk_t<\/em>. In questo caso, il blocco dati non viene copiato, ma il suo contatore di riferimenti viene incrementato. <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>Creazione di una copia leggera della tupla. I blocchi dati non vengono copiati, ma solo i loro contatori di riferimenti vengono incrementati. <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>Unione di tutti i messaggi della tupla in un unico messaggio:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,size_t len);<\/code><\/pre>\n<p><\/p>\n<p>Se l'argomento <em>len<\/em> \u00e8 uguale a -1, la dimensione del buffer riservato viene determinata automaticamente.<\/p>\n<p><\/p>\n<p>Rimozione del messaggio, della tupla:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void freemsg(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Il contatore di riferimenti del blocco dati viene decrementato di uno. Se raggiunge zero, il blocco dati viene anche eliminato.<\/p>\n<p><\/p>\n<p>Calcolo del volume totale dei dati nel messaggio o nella tupla.<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">size_t msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Estrazione del messaggio dalla coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *ms_queue_peek_last(q);<\/code><\/pre>\n<p><\/p>\n<p>Copia del contenuto dei campi riservati di un messaggio in un altro messaggio (in realt\u00e0 in questi campi si trovano flag utilizzati dallo 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\">Coda <em>queue_t<\/em><\/h3>\n<p><\/p>\n<p>La coda dei messaggi nel media streamer \u00e8 implementata come una lista doppiamente collegata circolare. Ogni elemento della lista contiene un puntatore a un blocco di dati con i campionamenti del segnale. In questo modo, vengono spostati solo i puntatori ai blocchi di dati, mentre i dati stessi rimangono fissi. Vale a dire, si spostano solo i riferimenti ad essi.<br \/>\nStruttura che descrive la coda <em>queue_t<\/em>, mostrata di seguito:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _queue\n{\n   mblk_t _q_stopper; \/* \"Elemento vuoto\" della coda, non punta ai dati, usato solo per gestire la coda. Durante l'inizializzazione della coda (qinit()), i suoi puntatori sono impostati per puntare a se stesso. *\/\n   int q_mcount;        \/\/ Numero di elementi nella coda.\n} queue_t;<\/code><\/pre>\n<p><\/p>\n<p>La struttura contiene un campo \u2013 un puntatore <em>_q_stopper<\/em> di tipo *mblk_t, che punta al primo elemento (messaggio) nella coda. Il secondo campo della struttura \u00e8 un contatore dei messaggi presenti nella coda.<br \/>\nNell'immagine seguente \u00e8 mostrata la coda con il nome q1, che contiene 4 messaggi m1, m2, m3, m4.<br \/>\n<img decoding=\"async\" alt=\"Studiamo il motore VoIP Mediastreamer2. Parte 11\" src=\"\/wp-content\/uploads\/2020\/04\/07fc043861edf15e5eb40e49c8ec5e3e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNell'immagine successiva \u00e8 mostrata la coda con il nome q1, che contiene 4 messaggi m1, m2, m3, m4. Il messaggio m2 \u00e8 la testa della tupla, a cui si collegano altri due messaggi m2_1 e m2_2.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Studiamo il motore VoIP Mediastreamer2. Parte 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\">Funzioni per la gestione delle code queue_t<\/h3>\n<p><\/p>\n<p>Inizializzazione della coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void qinit(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Campo <em>_q_stopper<\/em> (d'ora in poi lo chiameremo \"stopper\") \u00e8 inizializzato dalla funzione <em>mblk_init()<\/em>, il puntatore al precedente e al successivo elemento sono impostati in modo tale da puntare a se stesso. Il contatore degli elementi della coda viene azzerato.<\/p>\n<p><\/p>\n<p>Aggiunta di un nuovo elemento (messaggio):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void putq(queue_t *q, mblk_t *m);<\/code><\/pre>\n<p><\/p>\n<p>Il nuovo elemento <em>m<\/em> viene aggiunto alla fine della lista, i puntatori dell'elemento sono impostati in modo tale che lo stop diventi il suo successivo, e lui diventi il precedente dello stop. Il contatore degli elementi della coda viene incrementato.<\/p>\n<p><\/p>\n<p>Estrazione di un elemento dalla coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * getq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>viene estratto il messaggio che si trova dopo lo stop, il contatore degli elementi viene decrementato. Se nella coda, oltre allo stop, non ci sono altri elementi, viene restituito 0.<\/p>\n<p><\/p>\n<p>Inserimento di un messaggio nella coda:<\/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>Elemento <em>mp<\/em> viene inserito prima dell'elemento <em>emp<\/em>. Se <em>emp<\/em>=0, il messaggio viene aggiunto alla coda.<\/p>\n<p><\/p>\n<p>Estrazione del messaggio dalla testa della coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void remq(queue_t *q, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Il contatore degli elementi viene decrementato.<\/p>\n<p><\/p>\n<p>Lettura del puntatore al primo elemento nella coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * peekq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>Rimozione di tutti gli elementi dalla coda con la cancellazione degli stessi elementi:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void flushq(queue_t *q, int how);<\/code><\/pre>\n<p><\/p>\n<p>Argomento <em>how<\/em> non utilizzato. Il contatore degli elementi della coda \u00e8 impostato a zero.<\/p>\n<p><\/p>\n<p>Macro per leggere il puntatore all'ultimo elemento della coda:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * qlast(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Quando si lavora con le code dei messaggi, si deve tenere presente che chiamando <em>ms_queue_put(q, m)<\/em> con un puntatore nullo al messaggio, la funzione entra in un ciclo infinito. Il tuo programma si bloccher\u00e0. Si comporta in modo simile <em>ms_queue_next(q, m)<\/em>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-filtrov\">Collegamento dei filtri<\/h3>\n<p><\/p>\n<p>La coda descritta sopra viene utilizzata per trasmettere messaggi da un filtro a un altro o da uno a pi\u00f9 filtri. I filtri e le loro connessioni formano un grafo direzionale. L'ingresso o l'uscita di un filtro saranno nominati con il termine generico \"pin\". Per descrivere l'ordine delle connessioni tra i filtri, nello streamer media si utilizza il concetto di \"punto di segnale\". Il punto di segnale \u00e8 una struttura <em>_MSCPoint<\/em>, che contiene un puntatore al filtro e il numero di uno dei suoi pin, descrivendo quindi la connessione di uno degli ingressi o uscite del filtro. <\/p>\n<p><\/p>\n<h4 id=\"signalnaya-tochka-grafa-obrabotki-dannyh\">Punto di segnale del grafo di elaborazione dei dati<\/h4>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSCPoint{\nstruct _MSFilter *filter; \/\/ Puntatore al filtro del mediastreamer.\nint pin;                      \/\/ Numero di uno degli ingressi o uscite del filtro, ovvero il pin.\n} MSCPoint;\n<\/code><\/pre>\n<p><\/p>\n<p>I pin dei filtri sono numerati a partire da zero. <\/p>\n<p><\/p>\n<p>La connessione di due pin attraverso una coda di messaggi \u00e8 descritta dalla struttura <em>_MSQueue<\/em>, che contiene una coda di messaggi e puntatori a due punti di segnale che essa collega:<\/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>Chiameremo questa struttura il link di segnale. Ogni filtro del media streamer contiene una tabella di link di ingresso e una tabella di link di uscita (<em>MSQueue<\/em>). Le dimensioni delle tabelle sono definite al momento della creazione del filtro, come abbiamo gi\u00e0 fatto utilizzando la variabile esportata di tipo <em>MSFilterDesc<\/em>, quando abbiamo sviluppato il nostro filtro. Di seguito \u00e8 mostrata la struttura che descrive qualsiasi filtro nel media streamer, <em>MSFilter<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">\nstruct _MSFilter{\n    MSFilterDesc *desc;    \/* Puntatore al descrittore del filtro. *\/\n    \/* Attributi protetti, non possono essere spostati o rimossi altrimenti il funzionamento con i plugin sar\u00e0 compromesso. *\/\n    ms_mutex_t lock;      \/* Semaforo. *\/\n    MSQueue **inputs;     \/* Tabella dei collegamenti in ingresso. *\/\n    MSQueue **outputs;    \/* Tabella dei collegamenti in uscita. *\/\n    struct _MSFactory *factory; \/* Puntatore alla fabbrica che ha creato questa istanza del filtro. *\/\n    void *padding;              \/* Non utilizzato, sar\u00e0 utilizzato se verranno aggiunti campi protetti. *\/\n    void *data;                 \/* Puntatore a una struttura arbitraria per memorizzare lo stato interno del filtro e calcoli intermedi. *\/\n    struct _MSTicker *ticker;   \/* Puntatore all'oggetto tick, che non deve essere nullo quando viene chiamata la funzione process(). *\/\n    \/* attributi privati, possono essere spostati e cambiati in qualsiasi momento *\/\n    MSList *notify_callbacks; \/* Lista dei callback utilizzati per gestire gli eventi del filtro. *\/\n    uint32_t last_tick;       \/* Numero dell'ultimo tick in cui \u00e8 stata eseguita la chiamata a process(). *\/ \n    MSFilterStats *stats;     \/* Statistiche di funzionamento del filtro. *\/\n    int postponed_task; \/* Numero di attivit\u00e0 posticipate. Alcuni filtri possono posticipare l'elaborazione dei dati (chiamata a process()) per diversi tick. *\/\n    bool_t seen;  \/* Flag utilizzato dal ticker per contrassegnare che questa istanza del filtro \u00e8 gi\u00e0 stata gestita in questo tick. *\/\n};\ntypedef struct _MSFilter MSFilter;\n<\/code><\/pre>\n<p><\/p>\n<p>Dopo che abbiamo collegato i filtri nel programma C secondo il nostro progetto (ma senza collegare il ticker), abbiamo creato un grafo diretto, i cui nodi sono istanze della struttura <em>MSFilter<\/em>, mentre i bordi sono istanze dei link <em>MSQueue<\/em>. <\/p>\n<p><\/p>\n<h3 id=\"zakulisnaya-deyatelnost-tikera\">Attivit\u00e0 dietro le quinte del ticker<\/h3>\n<p><\/p>\n<p>Quando ti ho detto che il ticker \u00e8 un filtro fonte di impulsi, non era l'unica verit\u00e0 su di esso. Il ticker \u00e8 un oggetto che esegue l'avvio delle funzioni <em>process()<\/em> di tutti i filtri del grafo a cui \u00e8 connesso. Quando colleghiamo un ticker a un filtro del grafo nel programma C, mostriamo al ticker il grafo che da quel momento gestir\u00e0, fino a quando non lo scollegiamo. Dopo il collegamento, il ticker inizia a esaminare il grafo assegnatogli, compilando un elenco dei filtri di cui \u00e8 composto. Per non \"contare\" lo stesso filtro due volte, segna i filtri rilevati impostando un flag <em>seen<\/em>. La ricerca avviene attraverso le tabelle dei link, che sono presenti in ogni filtro. <\/p>\n<p><\/p>\n<p>Durante il suo tour di introduzione, il ticker verifica se tra i filtri ce n'\u00e8 almeno uno che funge da sorgente di blocchi di dati. Se non si trova nulla, il grafo \u00e8 considerato errato e il ticker termina l'esecuzione.<\/p>\n<p><\/p>\n<p>Se il grafo risulta \"corretto\", per ogni filtro trovato viene chiamata la funzione di inizializzazione <em>preprocess()<\/em>. Quando arriva il momento per il prossimo ciclo di elaborazione (ogni 10 millisecondi per impostazione predefinita), il ticker invoca la funzione <em>process()<\/em> per tutti i filtri sorgente precedentemente trovati e poi per gli altri filtri nella lista. Se un filtro ha collegamenti in ingresso, l'esecuzione della funzione <em>process()<\/em> si ripete finch\u00e9 le code dei link in entrata non sono vuote. Dopo di che, passa al filtro successivo della lista e \"scorre\" fino a liberare i link in entrata dai messaggi. Il ticker passa da un filtro all'altro finch\u00e9 la lista non termina. A questo punto finisce l'elaborazione del ciclo. <\/p>\n<p><\/p>\n<p>Ora torniamo alle tuple e parliamo del motivo per cui \u00e8 stata aggiunta una tale entit\u00e0 nel media streamer. In generale, il volume di dati necessario per l'algoritmo che opera all'interno del filtro non corrisponde e non \u00e8 multiplo delle dimensioni dei buffer di dati in ingresso. Ad esempio, scriviamo un filtro che esegue una trasformata di Fourier veloce, che per definizione pu\u00f2 elaborare solo blocchi di dati la cui dimensione \u00e8 una potenza di due. Supponiamo che siano 512 campioni. Se i dati vengono generati tramite un canale telefonico, allora il buffer dei dati di ciascun messaggio in ingresso ci porter\u00e0 160 campioni di segnale. C'\u00e8 la tentazione di non prelevare dati dall'ingresso finch\u00e9 non ci sar\u00e0 la quantit\u00e0 necessaria. Ma in questo caso si verificherebbe una collisione con il ticker, che tenterebbe invano di far scorrere il filtro fino a svuotare il link in ingresso. In precedenza abbiamo definito questa regola come il terzo principio di funzionamento del filtro. Secondo questo principio, la funzione process() del filtro deve prelevare tutti i dati dalle code di ingresso.<\/p>\n<p><\/p>\n<p>Oltre a ci\u00f2, non sar\u00e0 possibile prelevare solo 512 campioni dall'ingresso, poich\u00e9 \u00e8 necessario prelevare interi blocchi, ovvero il filtraggio dovr\u00e0 prelevare 640 campioni e utilizzare 512 di essi, mantenendo il resto fino al raggiungimento di un nuovo lotto di dati. Cos\u00ec, il nostro filtro, oltre al suo compito principale, deve garantire azioni ausiliarie per la memorizzazione temporanea dei dati in ingresso. Gli sviluppatori del media streamer e della soluzione a questo problema comune hanno progettato un oggetto speciale: MSBufferizer, che affronta questa sfida tramite le tuple. <\/p>\n<p><\/p>\n<h3 id=\"buferizator-msbufferizer\">Bufferizzatore (MSBufferizer)<\/h3>\n<p><\/p>\n<p>Questo oggetto accumuler\u00e0 i dati in ingresso all'interno del filtro e inizier\u00e0 a restituirli per l'elaborazione non appena la quantit\u00e0 di informazioni sar\u00e0 sufficiente per eseguire l'algoritmo del filtro. Mentre il buffer accumula dati, il filtro funzioner\u00e0 in modalit\u00e0 idle, senza utilizzare la capacit\u00e0 di calcolo della CPU. Ma non appena la funzione di lettura dal buffer restituir\u00e0 un valore diverso da zero, la funzione process() del filtro inizia a prelevare e a elaborare i dati dal buffer in porzioni della dimensione richiesta, fino al loro esaurimento.<br \/>\nI dati non ancora utilizzati rimangono nel buffer come primo elemento della tupla a cui si collegano i successivi blocchi di input. <\/p>\n<p><\/p>\n<p>Struttura che descrive il buffer: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">struct _MSBufferizer{\nqueue_t q; \/* Coda dei messaggi. *\/\nint size; \/* Dimensione totale dei dati attualmente nel buffer. *\/\n};\ntypedef struct _MSBufferizer MSBufferizer;<\/code><\/pre>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-msbufferizer\">Funzioni per la gestione di MSBufferizer<\/h3>\n<p><\/p>\n<p>Creazione di una nuova istanza del buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">MSBufferizer * ms_bufferizer_new(void);<\/code><\/pre>\n<p><\/p>\n<p>Viene allocata memoria, inizializzata in <em>ms_bufferizer_init()<\/em> e RESTITUISCE un puntatore.<\/p>\n<p><\/p>\n<p>Funzione di inizializzazione:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_init(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Viene inizializzata la coda <em>q<\/em>, campo <em>dimensione<\/em> impostata a zero.<\/p>\n<p><\/p>\n<p>Aggiunta di un messaggio:<\/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>Il messaggio m viene aggiunto alla coda. La dimensione calcolata dei blocchi di dati viene sommata a <em>dimensione<\/em>.<\/p>\n<p><\/p>\n<p>Trasferimento al buffer di tutti i messaggi nella coda dei dati del link <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>Il trasferimento dei messaggi dal link <em>q<\/em> nel buffer avviene tramite la funzione <em>ms_bufferizer_put()<\/em>.<\/p>\n<p><\/p>\n<p>Lettura dal buffer:<\/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>Se la dimensione dei dati accumulati nel buffer \u00e8 inferiore a quella richiesta (<em>datalen<\/em>), la funzione restituisce zero, la copia dei dati in data non viene effettuata. Altrimenti, avviene la copia sequenziale dei dati dai tuple presenti nel buffer. Dopo la copia, il tuple viene rimosso e la memoria viene liberata. La copia termina nel momento in cui vengono copiati datalen byte. Se lo spazio si esaurisce a met\u00e0 di un blocco di dati, in questo caso, il blocco di dati verr\u00e0 troncato alla parte rimanente non copiato. Alla successiva chiamata, la copia continuer\u00e0 da questo punto.<\/p>\n<p><\/p>\n<p>Lettura della quantit\u00e0 di dati disponibili in questo momento nel buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_get_avail(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Restituisce il campo <em>dimensione<\/em> del buffer.<\/p>\n<p><\/p>\n<p>Scarto di una parte dei dati presenti nel 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>La quantit\u00e0 specificata di byte di dati viene estratta e scartata. Vengono scartati i dati pi\u00f9 vecchi.<\/p>\n<p><\/p>\n<p>Rimozione di tutti i messaggi presenti nel buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_flush(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Il contatore dei dati viene azzerato.<\/p>\n<p><\/p>\n<p>Rimozione di tutti i messaggi presenti nel buffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_uninit(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>L'azzeramento del contatore non viene effettuato.<\/p>\n<p><\/p>\n<p>Rimozione del buffer e liberazione della memoria:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_destroy(MSBufferizer *obj);  <\/code><\/pre>\n<p><\/p>\n<p>Esempi di utilizzo del buffer possono essere trovati nel codice sorgente di diversi filtri del media streamer. Ad esempio, nel filtro MS_L16_ENC, che esegue la permutazione dei byte nei campioni dall'ordine di rete all'ordine dell'host:<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>Nell'articolo seguente, esamineremo la questione della valutazione del carico sul ticker e i modi per affrontare un'eccessiva carico computazionale nel media streamer.<\/p>\n<p>Fonte: <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 4.9.10 - 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. \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\" \/>\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\/it\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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. \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\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/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\udd47Esploriamo il motore VoIP Mediastreamer2. Parte 11 | ProHoster","description":"Il materiale dell'articolo \u00e8 tratto dal mio canale Zen. Meccanismo di trasferimento dati Blocco dati dblk_t Messaggio mblk_t Funzioni di gestione dei messaggi mblk_t Coda queue_t Funzioni di gestione delle code queue_t Connessione dei filtri Punto di segnale del grafo di elaborazione dei dati Attivit\u00e0 dietro le quinte del ticker Buffer (MSBufferizer) Funzioni di gestione di MSBufferizer Nell'articolo precedente abbiamo sviluppato il nostro filtro. Questo articolo sar\u00e0 dedicato al dispositivo","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","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. \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","og:url":"https:\/\/prohoster.info\/it\/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"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/79540","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=79540"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/79540\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/79541"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=79540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=79540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=79540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}