Il materiale dell'articolo è tratto dal mio .

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 segnalazione del grafo di elaborazione dati
- Attività dietro le quinte del ticker
- Bufferizzatore (MSBufferizer)
- Funzioni di gestione con MSBufferizer
Nell'articolo precedente Abbiamo sviluppato il nostro filtro. Questo articolo è dedicato al funzionamento del meccanismo interno di trasferimento dati tra i filtri dello stream media. Ciò consentirà in futuro di scrivere filtri sofisticati con meno sforzo.
Meccanismo di trasferimento dati
Il trasferimento di dati nello stream media avviene tramite code descritte dalla struttura queue_t. Le code trasferiscono sequenze di messaggi di tipo mblk_t, che di per sé non contengono dati di segnale, ma solo riferimenti al messaggio precedente, successivo e al blocco dati. Inoltre, voglio sottolineare che c'è anche un campo per un riferimento a un messaggio dello stesso tipo, che consente di organizzare una lista collegata di messaggi. Il gruppo di messaggi collegati in questo modo sarà chiamato tupla. Così, qualsiasi elemento della coda può essere un messaggio singolo mblk_t, oppure la testa di una tupla di messaggi mblk_t. Ogni messaggio della tupla può avere il proprio blocco dati sottostante. Di cosa servono le tuple ne discuteremo più avanti.
Come detto sopra, il messaggio di per sé non contiene un blocco di dati; contiene solo un puntatore all'area di memoria in cui si trova il blocco. In questa parte, l'immagine generale del funzionamento del mediastreamer ricorda il magazzino delle porte nel cartone animato "Monsters, Inc.", dove le porte (collegamenti ai dati - stanze) si muovono a una velocità folle lungo i nastri trasportatori, mentre le stanze stesse rimangono immobili.
Ora, seguendo l'gerarchia dal basso verso l'alto, esaminiamo in dettaglio le entità elencate dell meccanismo di trasferimento dati nello stream media.
Blocco dati dblk_t
Il blocco dati consiste in un intestazione e in un buffer di dati. L'intestazione è descritta dalla seguente struttura,
typedef struct datab
{
unsigned char *db_base; // Puntatore all'inizio del buffer di dati.
unsigned char *db_lim; // Puntatore alla fine del buffer di dati.
void (*db_freefn)(void*); // Funzione di liberazione della memoria alla rimozione del blocco.
int db_ref; // Contatore dei riferimenti.
} dblk_t;I campi della struttura contengono puntatori all'inizio del buffer, alla fine del buffer e alla funzione di eliminazione del buffer dati. L'ultimo elemento nell'intestazione db_ref — è il contatore dei riferimenti; quando raggiunge zero, questo segnala la rimozione di questo blocco dalla memoria. Se il blocco dati è stato creato dalla funzione datab_alloc() , il buffer dati sarà posizionato nella memoria subito dopo l'intestazione. In tutti gli altri casi, il buffer può trovarsi da qualche parte separato. Nel buffer dei dati saranno presenti le misurazioni del segnale o altri dati che vogliamo elaborare con i filtri.
Una nuova istanza del blocco dati viene creata con la funzione:
dblk_t *datab_alloc(int size);Come parametro di ingresso, riceve la dimensione dei dati che il blocco conserverà. Viene allocata più memoria per collocare all'inizio della memoria allocata l'intestazione — struttura datab. Ma quando si utilizzano altre funzioni, questo non avviene sempre; in alcuni casi, il buffer dati può trovarsi separato dall'intestazione del blocco dati. I campi della struttura vengono configurati al momento della creazione in modo che il suo campo db_base indichi l'inizio dell'area dati e db_lim indichi la sua fine. Il contatore dei riferimenti db_ref viene impostato su uno. Il puntatore alla funzione di eliminazione dei dati viene impostato a zero.
Messaggio mblk_t
Come già detto, gli elementi della coda sono di tipo mblk_t, definito come segue:
typedef struct msgb
{
struct msgb *b_prev; \/\/ Puntatore all'elemento precedente della lista.
struct msgb *b_next; \/\/ Puntatore all'elemento successivo della lista.
struct msgb *b_cont; \/\/ Puntatore per allegare ad un messaggio altri messaggi, per creare una tupla di messaggi.
struct datab *b_datap; \/\/ Puntatore alla struttura del blocco dati.
unsigned char *b_rptr; \/\/ Puntatore all'inizio dell'area dati per leggere i dati del buffer b_datap.
unsigned char *b_wptr; \/\/ Puntatore all'inizio dell'area dati per scrivere i dati del buffer b_datap.
uint32_t reserved1; \/\/ Campo riservato1, il mediastreamer colloca informazioni di servizio qui.
uint32_t reserved2; \/\/ Campo riservato2, il mediastreamer colloca informazioni di servizio qui.
#if defined(ORTP_TIMESTAMP)
struct timeval timestamp;
#endif
ortp_recv_addr_t recv_addr;
} mblk_t;Struttura mblk_t all'inizio contiene puntatori b_prev, b_next, necessari per organizzare una lista doppiamente collegata (che è la coda queue_t).
Poi c'è il puntatore b_cont, che viene utilizzato solo quando il messaggio entra nella tupla. Per l'ultimo messaggio nella tupla, questo puntatore rimane nullo.
Successivamente vediamo un puntatore al blocco dati b_datap, per il quale esiste il messaggio. Seguono i puntatori all'area all'interno del buffer dati del blocco. Il campo b_rptr indica il punto da cui i dati verranno letti dal buffer. Il campo b_wptr indica il punto da cui i dati verranno scritti nel buffer.
I campi rimanenti sono di natura operativa e non riguardano il funzionamento del meccanismo di trasmissione dei dati.
Di seguito è mostrato un singolo messaggio con il nome m1 e il blocco dati d1.

Nell'immagine successiva è illustrata una tupla di tre messaggi m1, m1_1, m1_2.

Le funzioni per lavorare con i messaggi mblk_t
Un nuovo messaggio mblk_t viene creato dalla funzione:
mblk_t *allocb(int size, int pri); essa allocca in memoria un nuovo messaggio mblk_t con un blocco dati di dimensione specificata size, il secondo argomento — pri non è utilizzato nella versione della libreria in esame. Deve rimanere nullo. Durante l'esecuzione della funzione verrà allocata memoria per la struttura del nuovo messaggio e verrà chiamata la funzione mblk_init(), che azzererà tutti i campi dell'istanza della struttura creata e poi, tramite la suddetta datab_alloc(), creerà il buffer dati. Successivamente verranno configurati i campi della struttura:
mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;In uscita otteniamo un nuovo messaggio con i campi inizializzati e un buffer dati vuoto. Per aggiungere dati al messaggio, è necessario copiarli nel buffer del blocco dati:
memcpy(msg->b_rptr, data, size);dove data — puntatore alla sorgente dei dati, e size — la loro dimensione.
Successivamente è necessario aggiornare il puntatore al punto di scrittura affinché punti nuovamente all'inizio dell'area libera nel buffer:
msg->b_wptr = msg->b_wptr + sizeSe è necessario creare un messaggio da un buffer già esistente, senza copiare, allora si utilizza la funzione:
mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); La funzione, dopo aver creato il messaggio e la struttura del blocco dati, configurerà i suoi puntatori ai dati all'indirizzo buf. Cioè, in questo caso, il buffer dati non si trova a seguire i campi dell'intestazione del blocco dati, come avveniva nella creazione del blocco dati tramite la funzione. datab_alloc(). Il buffer di dati passato alla funzione rimarrà dov'è, ma mediante puntatori sarà indirizzato all'intestazione del blocco dati appena creata, e quella a sua volta al messaggio.
A un messaggio mblk_t possono essere concatenati più blocchi di dati in modo sequenziale. Questo avviene tramite la funzione:
mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); mp è il messaggio a cui verrà aggiunto un altro blocco di dati;
data è un puntatore al blocco, di cui verrà fatta una copia nel messaggio;
size è la dimensione dei dati;
pad è il flag che indica se la dimensione della memoria allocata deve essere allineata a un confine di 4 byte (il riempimento verrà effettuato con zeri).
Se nel buffer di dati del messaggio c'è abbastanza spazio, i nuovi dati verranno concatenati ai dati già presenti. Se lo spazio disponibile nel buffer di dati del messaggio è inferiore a size, verrà creato un nuovo messaggio, con una dimensione del buffer adeguata, e i dati verranno copiati nel suo buffer. Questo nuovo messaggio viene agganciato a quello originale tramite un puntatore b_cont. In questo caso, il messaggio diventa una tupla.
Se nella tupla è necessario aggiungere un altro blocco di dati, bisogna usare la funzione:
void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);essa troverà l'ultimo messaggio nella tupla (avrà b_cont zero) e invokerà per quel messaggio la funzione appendb().
Per conoscere la dimensione dei dati in un messaggio o in una tupla, si può usare la funzione:
int msgdsize(const mblk_t *mp);essa esaminerà tutti i messaggi della tupla e restituirà il numero totale di dati nei buffer di dati di questi messaggi. Per ogni messaggio, la quantità di dati si calcola come:
mp->b_wptr - mp->b_rptrPer unire due tuple si utilizza la funzione:
mblk_t *concatb(mblk_t *mp, mblk_t *newm);essa attacca la tupla newm alla coda della tupla mp e restituisce un puntatore all'ultimo messaggio della tupla risultante.
Se necessario, la tupla può essere trasformata in un unico messaggio con un blocco di dati unico, ciò avviene tramite la funzione:
void msgpullup(mblk_t *mp,int len);se l'argomento len è uguale a -1, la dimensione del buffer allocato viene determinata automaticamente. Se len se è un numero positivo, verrà creato un buffer di questa dimensione e in esso verranno copiati i dati dei messaggi della tupla. Se il buffer si esaurisce, la copia verrà interrotta. Il primo messaggio della tupla riceverà un buffer di nuova dimensione con i dati copiati. Gli altri messaggi verranno eliminati e la memoria restituita all'heap.
Quando si elimina una struttura mblk_t si tiene conto del contatore dei riferimenti del blocco di dati, se durante la chiamata freeb() risulta essere zero, il buffer dei dati viene eliminato insieme all'istanza mblk_t, a cui punta.
Inizializzazione dei campi del nuovo messaggio:
void mblk_init(mblk_t *mp);Aggiunta a un messaggio di un'altra porzione di dati:
mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);Se i nuovi dati non possono essere inseriti nello spazio libero del buffer dei dati del messaggio, allora al messaggio viene allegato un messaggio creato separatamente con un buffer della dimensione necessaria (nel primo messaggio viene impostato un puntatore al messaggio aggiunto) e il messaggio si trasforma in una tupla.
Aggiunta di una porzione di dati alla tupla:
void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); La funzione chiama appendb() in un ciclo.
Unione di due tuple in una sola:
mblk_t *concatb(mblk_t *mp, mblk_t *newm);Messaggio newm verrà aggiunto a mp.
Creazione di una copia di un singolo messaggio:
mblk_t *copyb(const mblk_t *mp);Copia completa della tupla con tutti i blocchi di dati:
mblk_t *copymsg(const mblk_t *mp);Gli elementi della tupla vengono copiati dalla funzione copyb().
Creazione di una copia leggera del messaggio mblk_t. In questo caso, il blocco di dati non viene copiato, ma il contatore dei suoi riferimenti viene incrementato db_ref:
mblk_t *dupb(mblk_t *mp);Creazione di una copia leggera della tupla. I blocchi di dati non vengono copiati, vengono solo incrementati i loro contatori di riferimenti db_ref:
mblk_t *dupmsg(mblk_t* m);Unione di tutti i messaggi della tupla in un unico messaggio:
void msgpullup(mblk_t *mp,size_t len);Se l'argomento len è uguale a -1, la dimensione del buffer assegnato viene determinata automaticamente.
Rimozione del messaggio, della tupla:
void freemsg(mblk_t *mp);Il contatore dei riferimenti del blocco di dati viene decrementato di uno. Se raggiunge zero, anche il blocco di dati viene eliminato.
Calcolo del volume totale di dati nel messaggio o nella tupla.
size_t msgdsize(const mblk_t *mp);Estrazione del messaggio dalla coda:
mblk_t *ms_queue_peek_last (q);Copia del contenuto dei campi riservati di un messaggio in un altro messaggio (in realtà in questi campi si trovano le flag utilizzate dal media streamer):
mblk_meta_copy(const mblk_t *source, mblk *dest);Coda queue_t
La coda di messaggi nel media streamer è realizzata come una lista doppiamente collegata circolare. Ogni elemento della lista contiene un puntatore a un blocco di dati con le misurazioni del segnale. Ciò significa che si spostano solo i puntatori ai blocchi di dati, mentre i dati stessi rimangono fermi. Vale a dire, si muovono solo i riferimenti ad essi.
Struttura che descrive la coda queue_t, mostrata di seguito:
typedef struct _queue
{
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 vengono impostati in modo che puntino a se stesso. */
int q_mcount; // Numero di elementi nella coda.
} queue_t;La struttura contiene un campo - puntatore _q_stopper di tipo *mblk_t, che punta al primo elemento (messaggio) nella coda. Il secondo campo della struttura è un contatore dei messaggi presenti nella coda.
Nella figura sottostante è mostrata la coda di nome q1, contenente 4 messaggi m1, m2, m3, m4.

Nella figura successiva è mostrata la coda di nome q1, contenente 4 messaggi m1, m2, m3, m4. Il messaggio m2 è la testa della tupla, a cui si collegano altri due messaggi m2_1 e m2_2.

Funzioni di gestione delle code queue_t
Inizializzazione della coda:
void qinit(queue_t *q);Campo _q_stopper (d'ora in poi lo chiameremo "stopper") è inizializzato dalla funzione mblk_init(), il suo puntatore all'elemento precedente e successivo viene configurato in modo che punti a se stesso. Il contatore degli elementi nella coda viene azzerato.
Aggiunta di un nuovo elemento (messaggio):
void putq(queue_t *q, mblk_t *m);Il nuovo elemento m viene aggiunto alla fine della lista, i puntatori dell'elemento vengono configurati in modo che lo stopper diventi l'elemento successivo per lui, mentre lui diventa l'elemento precedente per lo stopper. Il contatore degli elementi nella coda viene incrementato.
Estrazione di un elemento dalla coda:
mblk_t * getq(queue_t *q); viene estratto il messaggio che si trova dopo lo stopper, il contatore degli elementi viene decrementato. Se nella coda, oltre allo stopper, non ci sono altri elementi, viene restituito 0.
Inserimento di un messaggio nella coda:
void insq(queue_t *q, mblk_t *emp, mblk_t *mp); Elemento mp viene inserito prima dell'elemento empSe emp=0, quindi il messaggio viene aggiunto alla coda.
Estrazione del messaggio dalla testa della coda:
void remq(queue_t *q, mblk_t *mp); Il contatore degli elementi viene decrementato.
Lettura del puntatore al primo elemento nella coda:
mblk_t * peekq(queue_t *q); Cancellazione di tutti gli elementi dalla coda con eliminazione degli stessi elementi:
void flushq(queue_t *q, int how);Argomento how non utilizzato. Il contatore degli elementi della coda è impostato a zero.
Macro per leggere il puntatore all'ultimo elemento della coda:
mblk_t * qlast(queue_t *q);Quando si lavora con le code di messaggi, è importante tenere presente che con la chiamata ms_queue_put(q, m) con un puntatore nullo al messaggio, la funzione va in loop. Il tuo programma si bloccherà. Si comporta allo stesso modo ms_queue_next(q, m).
Connessione dei filtri
La coda descritta sopra è utilizzata per inviare messaggi da un filtro a un altro o da uno a più filtri. I filtri e le loro connessioni formano un grafo diretto. L'ingresso o l'uscita del filtro sarà chiamato con il termine generico "pin". Per descrivere l'ordine delle connessioni tra i filtri, nel mediastreamer si usa il concetto di "punto di segnale". Il punto di segnale è una struttura _MSCPoint, che contiene un puntatore al filtro e il numero di uno dei suoi pin, descrivendo così la connessione di uno degli ingressi o delle uscite del filtro.
Punto di segnalazione del grafo di elaborazione dati
typedef struct _MSCPoint{
struct _MSFilter *filter; \/\/ Puntatore al filtro del mediastreamer.
int pin; \/\/ Numero di uno degli ingressi o delle uscite del filtro, ovvero un pin.
} MSCPoint;
I pin dei filtri sono numerati a partire da zero.
La connessione di due pin tramite la coda di messaggi è descritta dalla struttura _MSQueue, che contiene la coda dei messaggi e puntatori a due punti di segnale che connette:
typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;
Ci riferiremo a questa struttura come link di segnale. Ogni filtro del mediastreamer contiene una tabella di link di ingresso e una tabella di link di uscita (MSQueue). La dimensione delle tabelle è definita al momento della creazione del filtro, ciò che abbiamo già fatto tramite la variabile esportata di tipo MSFilterDesc, quando abbiamo sviluppato il nostro filtro. Di seguito è mostrata la struttura che descrive qualsiasi filtro nel mediastreamer, MSFilter:
struct _MSFilter{
MSFilterDesc *desc;
/* Puntatore al descrittore del filtro. */
/* Attributi protetti, non possono essere spostati o rimossi altrimenti il funzionamento con i plugin sarà compromesso. */
ms_mutex_t lock;
/* Semaforo. */
MSQueue **inputs;
/* Tabella dei collegamenti di input. */
MSQueue **outputs;
/* Tabella dei collegamenti di output. */
struct _MSFactory *factory;
/* Puntatore alla fabbrica che ha creato questa istanza del filtro. */
void *padding;
/* Non utilizzato, sarà attivato se saranno aggiunti campi protetti. */
void *data;
/* Puntatore a una struttura arbitraria per memorizzare i dati dello stato interno del filtro e i calcoli intermedi. */
struct _MSTicker *ticker;
/* Puntatore all'oggetto ticker, che non deve essere nullo quando viene chiamata la funzione process(). */
/* attributi privati, possono essere spostati e modificati in qualsiasi momento */
MSList *notify_callbacks;
/* Elenco delle callback utilizzate per gestire gli eventi del filtro. */
uint32_t last_tick;
/* Numero dell'ultimo tick quando è stata eseguita la chiamata process(). */
MSFilterStats *stats;
/* Statistiche sul funzionamento del filtro. */
int postponed_task; /* Numero di attività posticipate. Alcuni filtri possono posticipare l'elaborazione dei dati (chiamata process()) per diversi tick. */
bool_t seen; /* Flag utilizzato dal ticker per contrassegnare che questa istanza del filtro è già stata trattata in questo tick. */
};
typedef struct _MSFilter MSFilter;
Dopo aver collegato i filtri nel nostro programma C secondo la nostra concezione (ma senza collegare il ticker), abbiamo così creato un grafo orientato, i cui nodi sono istanze della struttura MSFilter, e gli archi sono istanze dei collegamenti MSQueue.
Attività dietro le quinte del ticker
Quando ti ho detto che il ticker è un filtro sorgente di tick, non era tutta la verità su di lui. Il ticker è un oggetto che esegue l'avvio delle funzioni in base agli orari process() di tutti i filtri dello schema (grafo) a cui è collegato. Quando colleghiamo un ticker a un filtro del grafo in un programma C, stiamo mostrando al ticker il grafo che da quel momento gestirà, fino a quando non lo disconnetteremo. Dopo il collegamento, il ticker inizia a esaminare il grafo che ha sotto la sua cura, compilando un elenco dei filtri a cui è collegato. Per non "contare" due volte lo stesso filtro, segna i filtri individuati, impostando un flag su di essi seen. La ricerca avviene nelle tabelle dei collegamenti che ha ogni filtro.
Durante il suo tour di orientamento tramite il grafico, il ticker verifica se c'è almeno un filtro che svolge la funzione di sorgente dei blocchi di dati. Se tali filtri non vengono trovati, il grafico viene considerato errato e il ticker termina l'esecuzione in modo anomalo.
Se il grafo è "corretto", per ciascun filtro trovato viene chiamata la funzione di inizializzazione preprocess(). Non appena arriva il momento dell'iterazione successiva di elaborazione (di default ogni 10 millisecondi), il ticker richiama la funzione process() per tutti i filtri sorgente trovati in precedenza, e poi per gli altri filtri nell'elenco. Se un filtro ha link di ingresso, il lancio della funzione process() si ripete finché le code dei collegamenti in ingresso non sono vuote. Dopo di che, passa al filtro successivo nell'elenco e "scrolla" fino a liberare i collegamenti in ingresso dai messaggi. Il ticker passa da un filtro all'altro fino a quando non finisce l'elenco. La gestione del ciclo termina qui.
Ora torniamo alle tupla e parliamo del motivo per cui è stata aggiunta una tale entità nello stream di dati. In generale, la quantità di dati richiesta dall'algoritmo che opera all'interno del filtro non coincide e non è un multiplo della dimensione dei buffer di dati che arrivano in ingresso. Ad esempio, scriviamo un filtro che esegue una veloce trasformazione di Fourier, la quale per definizione può elaborare solo blocchi di dati la cui dimensione è una potenza di due. Supponiamo sia 512 campioni. Se i dati vengono generati tramite un canale telefonico, il buffer di dati di ogni messaggio in ingresso ci porterà 160 campioni di segnale. C'è la tentazione di non prelevare dati dall'ingresso finché non ci sarà la quantità necessaria. Ma in questo caso ci sarà una collisione con il ticker, che cercherà invano di far avanzare il filtro fino a svuotare il link di 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.
Oltre a questo, non sarà possibile prelevare solo 512 campioni all'ingresso, poiché i campioni possono essere prelevati solo in blocchi interi, cioè il filtro dovrà prelevare 640 campioni e, utilizzando 512 di essi, il resto rimarrà in attesa di un nuovo accumulo di dati. In questo modo, il nostro filtro, oltre al suo lavoro principale, deve garantire azioni ausiliarie per l'immagazzinamento temporaneo dei dati in entrata. Gli sviluppatori dello streaming multimediale, per risolvere questo problema comune, hanno creato un oggetto speciale — MSBufferizer (buffer), che affronta questa questione tramite tuple.
Bufferizzatore (MSBufferizer)
Questo oggetto accumulerà i dati in ingresso all'interno del filtro e inizierà a passarli in elaborazione non appena la quantità di informazioni sarà sufficiente per l'esecuzione dell'algoritmo del filtro. Finché il buffer accumula dati, il filtro funzionerà in modalità idle, senza utilizzare potenza di calcolo della CPU. Ma quando la funzione di lettura dal buffer restituirà un valore diverso da zero, la funzione process() del filtro inizierà a prelevare e elaborare i dati dal buffer in porzioni della dimensione necessaria, fino all'esaurimento.
I dati ancora non utilizzati rimangono nel buffer come primo elemento della tupla, a cui si attaccano i successivi blocchi di dati in ingresso.
Struttura che descrive il buffer:
struct _MSBufferizer{
queue_t q; /* Coda dei messaggi. */
int size; /* Dimensione totale dei dati attualmente presenti nel buffer. */
};
typedef struct _MSBufferizer MSBufferizer;Funzioni di gestione con MSBufferizer
Creazione di una nuova istanza del buffer:
MSBufferizer * ms_bufferizer_new(void);Viene allocata memoria, inizializzata in ms_bufferizer_init() e restituisce un puntatore.
Funzione di inizializzazione:
void ms_bufferizer_init(MSBufferizer *obj); Si inizializza la coda q, il campo size viene impostato a zero.
Aggiunta di un messaggio:
void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); Il messaggio m viene aggiunto alla coda. La dimensione calcolata dei blocchi di dati viene aggiunta a size.
Trasferimento nel buffer di tutti i messaggi della coda dei dati del link q:
void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q); Il trasferimento dei messaggi dal link q nel buffer avviene tramite la funzione ms_bufferizer_put().
Lettura dal buffer:
int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); Se la dimensione dei dati accumulati nel buffer risulta inferiore a quella richiesta (datalen), la funzione restituisce zero, la copia dei dati in data non viene eseguita. Altrimenti, viene eseguita una copia sequenziale dei dati dalle tuple presenti nel buffer. Dopo la copia, la tupla viene rimossa e la memoria liberata. La copia termina quando vengono copiati datalen byte. Se lo spazio finisce a metà di un blocco di dati, in questo messaggio, il blocco di dati sarà accorciato alla parte rimanente non copiata. Alla chiamata successiva, la copia continuerà da questo punto.
Lettura della quantità di dati disponibili al momento nel buffer:
int ms_bufferizer_get_avail(MSBufferizer *obj); Restituisce il campo size del buffer.
Scarto di parte dei dati presenti nel buffer:
void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);Il numero specificato di byte di dati viene estratto e scartato. Vengono scartati i dati più vecchi.
Eliminazione di tutti i messaggi presenti nel buffer:
void ms_bufferizer_flush(MSBufferizer *obj); Il contatore dei dati viene azzerato.
Eliminazione di tutti i messaggi presenti nel buffer:
void ms_bufferizer_uninit(MSBufferizer *obj); L'azzeramento del contatore non viene eseguito.
Eliminazione del buffer e liberazione della memoria:
void ms_bufferizer_destroy(MSBufferizer *obj); 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 riorganizzazione dei byte nei campioni dal formato di rete, al formato host:
Nell'articolo seguente, esamineremo la questione della valutazione del carico sul ticker e i metodi per affrontare un'eccessiva carico computazionale nel media streamer.
Fonte: habr.com
