Esplorare il motore VoIP di Mediastreamer2. Parte 11

Il materiale dell'articolo è tratto dal mio canale zen.

Esplorare il motore VoIP di Mediastreamer2. Parte 11

Meccanismo di spostamento dei dati

  • Blocco dati dblk_t
  • Messaggio mblk_t
  • Funzioni per lavorare con i messaggi mblk_t
  • Coda coda_t
  • Funzioni per lavorare con le code Queue_t
  • Collegamento dei filtri
  • Punto del segnale del grafico di elaborazione dei dati
  • Attività dietro le quinte del ticker
  • Bufferizzatore (MSBufferizer)
  • Funzioni per lavorare con MSBufferizer

Nel passato Articolo abbiamo sviluppato il nostro filtro. Questo articolo si concentrerà sul meccanismo interno per lo spostamento dei dati tra i filtri dello streamer multimediale. Ciò ti consentirà di scrivere filtri sofisticati con meno sforzo in futuro.

Meccanismo di spostamento dei dati

Lo spostamento dei dati nello streamer multimediale viene eseguito utilizzando le code descritte dalla struttura coda_t. Stringhe di messaggi come mblk_t, che di per sé non contengono dati di segnale, ma si collegano solo al messaggio precedente, successivo e al blocco dati. Inoltre, voglio sottolineare in particolare che esiste anche un campo per il collegamento a un messaggio dello stesso tipo, che consente di organizzare un elenco di messaggi collegati singolarmente. Chiameremo tupla un gruppo di messaggi uniti da tale lista. Pertanto, qualsiasi elemento della coda può essere un singolo messaggio mblk_t, e forse l'inizio di una tupla di messaggi mblk_t. Ogni messaggio tupla può avere il proprio blocco dati di reparto. Discuteremo più avanti il ​​motivo per cui le tuple sono necessarie.

Come accennato in precedenza, il messaggio stesso non contiene un blocco di dati; contiene invece solo un puntatore all'area di memoria in cui è memorizzato il blocco. In questa parte, il quadro generale del lavoro del media streamer ricorda il magazzino delle porte del cartone animato "Monsters, Inc.", dove le porte (collegamenti a dati - stanze) si muovono a una velocità folle lungo i trasportatori aerei, mentre le stanze stesse restare immobile.

Ora, spostandoci lungo la gerarchia dal basso verso l'alto, consideriamo in dettaglio le entità elencate del meccanismo di trasmissione dei dati nel media streamer.

Blocco dati dblk_t

Il blocco dati è costituito da un'intestazione e da un buffer dati. L'intestazione è descritta dalla seguente struttura,

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

I campi della struttura contengono puntatori all'inizio del buffer, alla fine del buffer e alla funzione per cancellare il buffer dei dati. Ultimo elemento nell'intestazione db_rif — contatore dei riferimenti, se arriva a zero, serve come segnale per cancellare questo blocco dalla memoria. Se il blocco dati è stato creato dalla funzione datab_alloc() , il buffer dei dati verrà posizionato in memoria immediatamente dopo l'intestazione. In tutti gli altri casi, il buffer può essere posizionato da qualche parte separatamente. Il buffer dei dati conterrà campioni di segnale o altri dati che vogliamo elaborare con filtri.

Una nuova istanza di un blocco dati viene creata utilizzando la funzione:

dblk_t *datab_alloc(int size);

Come parametro di input viene data la dimensione dei dati che il blocco memorizzerà. Viene allocata più memoria per posizionare un'intestazione - struttura - all'inizio della memoria allocata datab. Ma quando si utilizzano altre funzioni, questo non sempre accade; in alcuni casi il buffer dei dati può trovarsi separatamente dall'intestazione del blocco dati. Quando si crea una struttura, i campi vengono configurati in modo che il suo field db_base indicava l'inizio dell'area dati e db_lim fino alla sua fine. Conteggio dei collegamenti db_rif è impostato su uno. Il puntatore della funzione di cancellazione dei dati è impostato su zero.

Messaggio mblk_t

Come affermato, gli elementi della coda sono di tipo mblk_t, è definito come segue:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struttura mblk_t contiene puntatori all'inizio b_prev, b_successivo, necessari per organizzare una lista doppiamente concatenata (che è una coda coda_t).

Poi arriva il puntatore b_cont, che viene utilizzato solo quando il messaggio fa parte di una tupla. Per l'ultimo messaggio nella tupla, questo puntatore rimane nullo.

Successivamente vediamo un puntatore a un blocco dati b_datap, per il quale esiste il messaggio. È seguito da puntatori all'area all'interno del buffer dei dati del blocco. Campo b_rptr specifica la posizione da cui verranno letti i dati dal buffer. Campo b_wptr indica la posizione da cui verranno eseguite le scritture nel buffer.

I restanti campi sono di natura di servizio e non riguardano il funzionamento del meccanismo di trasferimento dei dati.

Di seguito è riportato un singolo messaggio con il nome m1 e blocco dati d1.
Esplorare il motore VoIP di Mediastreamer2. Parte 11
La figura seguente mostra una tupla di tre messaggi m1, m1_1, m1_2.
Esplorare il motore VoIP di Mediastreamer2. Parte 11

Funzioni di messaggistica mblk_t

Un nuovo messaggio mblk_t creato dalla funzione:

mblk_t *allocb(int size, int pri); 

inserisce un nuovo messaggio nella memoria mblk_t con un blocco dati della dimensione specificata Taglia, secondo argomento - pri non utilizzato in questa versione della libreria. Dovrebbe rimanere zero. Durante l'esecuzione della funzione, verrà allocata memoria per la struttura del nuovo messaggio e verrà chiamata la funzione mblk_init(), che ripristinerà tutti i campi dell'istanza creata della struttura e quindi, utilizzando quanto sopra menzionato datab_alloc(), creerà un buffer di dati. Dopodiché verranno configurati i campi presenti nella 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 riceviamo un nuovo messaggio con campi inizializzati e un buffer dati vuoto. Per aggiungere dati a un messaggio, è necessario copiarlo nel buffer del blocco dati:

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

dove dati è un puntatore all'origine dati e Taglia - la loro dimensione.
quindi è necessario aggiornare il puntatore al punto di scrittura in modo che punti nuovamente all'inizio dell'area libera nel buffer:

msg->b_wptr = msg->b_wptr + size

Se devi creare un messaggio da un buffer esistente, senza copiarlo, 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. Quelli. in questo caso il buffer dati non si trova dopo i campi di intestazione del blocco dati, come avveniva durante la creazione di un blocco dati con la funzione datab_alloc(). Il buffer con i dati passati alla funzione rimarrà dov'era, ma con l'aiuto dei puntatori verrà collegato all'intestazione appena creata del blocco dati e, di conseguenza, al messaggio.

A un messaggio mblk_t È possibile concatenare più blocchi dati in sequenza. Questo viene fatto dalla funzione:

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

mp — un messaggio al quale verrà aggiunto un altro blocco di dati;
dati — puntatore al blocco, una copia del quale verrà aggiunta al messaggio;
Taglia — dimensione dei dati;
sentiero — un flag che indica che la dimensione della memoria allocata deve essere allineata lungo un limite di 4 byte (il riempimento verrà eseguito con zeri).

Se c'è abbastanza spazio nel buffer dei dati dei messaggi esistente, i nuovi dati verranno incollati dietro i dati già presenti. Se nel buffer dei dati dei messaggi c'è meno spazio libero di Taglia, viene creato un nuovo messaggio con una dimensione del buffer sufficiente e i dati vengono copiati nel relativo buffer. Questo è un nuovo messaggio, collegato a quello originale tramite un puntatore b_cont. In questo caso, il messaggio si trasforma in una tupla.

Se devi aggiungere un altro blocco di dati alla tupla, devi utilizzare la funzione:

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

troverà l'ultimo messaggio nella tupla (ha b_cont sarà nullo) e chiamerà la funzione per questo messaggio appendb().

Puoi scoprire la dimensione dei dati in un messaggio o in una tupla usando la funzione:

int msgdsize(const mblk_t *mp);

eseguirà il loop di tutti i messaggi nella tupla e restituirà la quantità totale di dati nei buffer di dati di tali messaggi. Per ciascun messaggio, la quantità di dati viene calcolata come segue:

 mp->b_wptr - mp->b_rptr

Per combinare due tuple, utilizzare la funzione:

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

aggiunge la tupla nuovo alla coda della tupla mp e restituisce un puntatore all'ultimo messaggio della tupla risultante.

Se necessario, una tupla può essere trasformata in un messaggio con un singolo blocco di dati; questo viene fatto dalla funzione:

void msgpullup(mblk_t *mp,int len);

se argomento len è -1, la dimensione del buffer allocato viene determinata automaticamente. Se len è un numero positivo, verrà creato un buffer di questa dimensione e i dati del messaggio tupla verranno copiati al suo interno. Se il buffer si esaurisce, la copia si fermerà lì. Il primo messaggio della tupla riceverà un buffer di nuova dimensione con i dati copiati. I messaggi rimanenti verranno eliminati e la memoria verrà ripristinata nell'heap.

Quando si elimina una struttura mblk_t il conteggio dei riferimenti del blocco dati viene preso in considerazione se, durante il richiamo libera() risulta essere zero, quindi il buffer dei dati viene eliminato insieme all'istanza mblk_t, che lo indica.

Inizializzazione dei campi di un nuovo messaggio:

void mblk_init(mblk_t *mp);

Aggiunta di un altro dato al messaggio:

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

Se i nuovi dati non entrano nello spazio libero del buffer dei dati del messaggio, al messaggio viene allegato un messaggio creato separatamente con un buffer della dimensione richiesta (nel primo messaggio viene impostato un puntatore al messaggio aggiunto) e il messaggio si trasforma in una tupla.

Aggiunta di un dato a una tupla:

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

La funzione chiama appendb() in un ciclo.

Combinando due tuple in una:

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

Messaggio nuovo sarà allegato a mp.

Creazione di una copia di un singolo messaggio:

mblk_t *copyb(const mblk_t *mp);

Copia completa di una tupla con tutti i blocchi di dati:

mblk_t *copymsg(const mblk_t *mp);

Gli elementi della tupla vengono copiati dalla funzione copyb().

Crea una copia semplice di un messaggio mblk_t. In questo caso il blocco dati non viene copiato ma viene incrementato il suo contatore di riferimenti db_rif:

mblk_t *dupb(mblk_t *mp);

Creare una copia leggera di una tupla. I blocchi dati non vengono copiati, vengono incrementati solo i loro contatori di riferimento db_rif:

mblk_t *dupmsg(mblk_t* m);

Incollare tutti i messaggi di una tupla in un unico messaggio:

void msgpullup(mblk_t *mp,size_t len);

Se l'argomento len è -1, la dimensione del buffer allocato viene determinata automaticamente.

Eliminazione di un messaggio, tupla:

void freemsg(mblk_t *mp);

Il conteggio dei riferimenti del blocco dati viene diminuito di uno. Se raggiunge lo zero viene cancellato anche il blocco dati.

Calcolo della quantità totale di dati in un messaggio o tupla.

size_t msgdsize(const mblk_t *mp);

Recupero di un messaggio dalla coda della coda:

mblk_t *ms_queue_peek_last (q);

Copiare il contenuto dei campi riservati di un messaggio in un altro messaggio (infatti questi campi contengono flag che vengono utilizzati dal media streamer):

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

Turno coda_t

La coda dei messaggi nello streamer multimediale è implementata come un elenco circolare doppiamente collegato. Ogni elemento della lista contiene un puntatore a un blocco dati con campioni di segnale. Si scopre che solo i puntatori al blocco dati si muovono a turno, mentre i dati stessi rimangono immobili. Quelli. vengono spostati solo i collegamenti ad essi.
Struttura che descrive la coda coda_t, mostrato di seguito:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

La struttura contiene un campo: un puntatore _q_stopper digitare *mblk_t, punta al primo elemento (messaggio) nella coda. Il secondo campo della struttura è il contatore dei messaggi in coda.
La figura seguente mostra una coda denominata q1 contenente 4 messaggi m1, m2, m3, m4.
Esplorare il motore VoIP di Mediastreamer2. Parte 11
La figura seguente mostra una coda denominata q1 contenente 4 messaggi m1,m2,m3,m4. Il messaggio m2 è l'inizio di una tupla che contiene altri due messaggi m2_1 e m2_2.

Esplorare il motore VoIP di Mediastreamer2. Parte 11

Funzioni per lavorare con le code Queue_t

Inizializzazione della coda:

void qinit(queue_t *q);

Campo _q_stopper (di seguito lo chiameremo “stopper”) viene inizializzato dalla funzione mblk_init(), il suo elemento precedente e il puntatore dell'elemento successivo vengono regolati per puntare a se stesso. Il contatore degli elementi della coda viene reimpostato su zero.

Aggiunta di un nuovo elemento (messaggi):

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

Nuovo elemento m viene aggiunto alla fine dell'elenco, i puntatori degli elementi vengono regolati in modo che il fermo diventi l'elemento successivo e diventi l'elemento precedente per il fermo. Il contatore degli elementi della coda viene incrementato.

Recupero di un elemento dalla coda:

mblk_t * getq(queue_t *q); 

Il messaggio che arriva dopo che il tappo è stato recuperato e il contatore dell'elemento è stato decrementato. Se non ci sono elementi nella coda tranne lo stopper, viene restituito 0.

Inserimento di un messaggio in una coda:

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

elemento mp inserito prima dell'elemento emp. Se emp=0, il messaggio viene aggiunto alla coda della coda.

Recupero di un messaggio dal capo coda:

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

Il contatore degli elementi viene decrementato.

Lettura di un puntatore al primo elemento della coda:

mblk_t * peekq(queue_t *q); 

Rimozione di tutti gli elementi dalla coda eliminando gli elementi stessi:

void flushq(queue_t *q, int how);

argomento come non usato. Il contatore degli elementi della coda è impostato su zero.

Macro per leggere un puntatore all'ultimo elemento della coda:

mblk_t * qlast(queue_t *q);

Quando lavori con le code di messaggi, tieni presente che quando chiami ms_queue_put(q, m) con un puntatore nullo al messaggio, la funzione si ripete. Il tuo programma si bloccherà. si comporta in modo simile ms_coda_successivo(q, m).

Collegamento dei filtri

La coda sopra descritta viene utilizzata per passare i messaggi da un filtro all'altro o da uno a più filtri. I filtri e le loro connessioni formano un grafico diretto. L'ingresso o l'uscita del filtro verrà chiamato con la parola generica "pin". Per descrivere l'ordine in cui i filtri sono collegati tra loro, lo streamer multimediale utilizza il concetto di "punto di segnale". Il punto del segnale è la struttura _MSCPoint, che contiene un puntatore al filtro e il numero di uno dei suoi pin; descrive quindi la connessione di uno degli ingressi o delle uscite del filtro.

Punto del segnale del grafico di elaborazione dei dati

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

I pin del filtro sono numerati a partire da zero.

La connessione di due pin tramite una coda di messaggi è descritta dalla struttura _MSQueue, che contiene una coda di messaggi e puntatori ai due punti di segnale che collega:

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

Chiameremo questa struttura collegamento di segnale. Ciascun filtro dello streamer multimediale contiene una tabella di collegamenti di input e una tabella di collegamenti di output (MSQueue). La dimensione delle tabelle viene impostata durante la creazione di un filtro; lo abbiamo già fatto utilizzando una variabile esportata di tipo MSFilterDesc, quando abbiamo sviluppato il nostro filtro. Di seguito è riportata una struttura che descrive qualsiasi filtro in uno streamer multimediale, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Dopo aver collegato i filtri nel programma C secondo il nostro piano (ma non collegato il ticker), abbiamo quindi creato un grafico diretto, i cui nodi sono istanze della struttura MSFiltere gli spigoli sono istanze di collegamenti MSQueue.

Attività dietro le quinte del ticker

Quando ti ho detto che il ticker è un filtro per la fonte dei tick, non era tutta la verità. Un ticker è un oggetto che esegue funzioni sull'orologio processi() tutti i filtri del circuito (grafico) a cui è collegato. Quando colleghiamo un ticker a un filtro grafico in un programma C, mostriamo al ticker il grafico che controllerà da ora in poi fino a quando non lo spegneremo. Dopo essersi connesso, il ticker comincia ad esaminare il grafico affidato alle sue cure, compilando una lista di filtri che lo includono. Per non “contare” due volte lo stesso filtro, contrassegna i filtri rilevati inserendo una casella di controllo al loro interno visto. La ricerca viene effettuata utilizzando le tabelle di collegamento di cui dispone ciascun filtro.

Durante il suo giro introduttivo al grafico, il ticker controlla se tra i filtri ce n'è almeno uno che funge da fonte di blocchi di dati. Se non ce ne sono, il grafico viene considerato errato e il ticker si blocca.

Se il grafico risulta “corretto”, per ogni filtro trovato, la funzione viene richiamata per l'inizializzazione preelaborazione(). Non appena arriva il momento per il ciclo di elaborazione successivo (ogni 10 millisecondi per impostazione predefinita), il ticker richiama la funzione processi() per tutti i filtri di origine trovati in precedenza, quindi per i restanti filtri nell'elenco. Se il filtro ha collegamenti di input, viene eseguita la funzione processi() si ripete finché le code dei collegamenti di input non sono vuote. Successivamente, passa al filtro successivo nell'elenco e lo “scorre” finché i collegamenti di input non sono privi di messaggi. Il ticker si sposta da un filtro all'altro fino al termine dell'elenco. Questo completa l'elaborazione del ciclo.

Ora torneremo alle tuple e parleremo del motivo per cui tale entità è stata aggiunta allo streamer multimediale. In generale la quantità di dati richiesti dall'algoritmo operante all'interno del filtro non coincide e non è un multiplo della dimensione dei buffer di dati ricevuti in ingresso. Ad esempio, stiamo scrivendo un filtro che esegue una trasformata veloce di Fourier, che per definizione può elaborare solo blocchi di dati la cui dimensione è una potenza di due. Lascia che siano 512 conteggi. Se i dati vengono generati da un canale telefonico, il buffer di dati di ciascun messaggio in ingresso ci porterà 160 campioni di segnale. È forte la tentazione di non raccogliere dati dall'input finché non è presente la quantità di dati richiesta. Ma in questo caso, si verificherà una collisione con il ticker, che tenterà senza successo di scorrere il filtro finché il collegamento di input non sarà vuoto. In precedenza, abbiamo designato questa regola come il terzo principio del filtro. Secondo questo principio, la funzione process() del filtro deve prendere tutti i dati dalle code di input.

Inoltre non sarà possibile prelevare solo 512 campioni dall'input, poiché si potranno prelevare solo blocchi interi, cioè il filtro dovrà prelevare 640 campioni e utilizzarne 512, il resto prima di accumulare una nuova porzione di dati. Pertanto, il nostro filtro, oltre al suo lavoro principale, deve fornire azioni ausiliarie per la memorizzazione intermedia dei dati di input. Gli sviluppatori del media streamer e della soluzione a questo problema generale hanno sviluppato un oggetto speciale: MSBufferizer (bufferer), che risolve questo problema utilizzando le tuple.

Bufferizzatore (MSBufferizer)

Questo è un oggetto che accumulerà i dati di input all'interno del filtro e inizierà a inviarli per l'elaborazione non appena la quantità di informazioni sarà sufficiente per eseguire l'algoritmo del filtro. Mentre il buffer accumula dati, il filtro funzionerà in modalità inattiva, senza consumare la potenza di elaborazione del processore. Ma non appena la funzione di lettura dal buffer restituisce un valore diverso da zero, la funzione process() del filtro inizia a prendere ed elaborare i dati dal buffer in porzioni della dimensione richiesta, fino al suo esaurimento.
I dati non ancora richiesti rimangono nel buffer come primo elemento della tupla, a cui sono allegati i successivi blocchi di dati di input.

La struttura che descrive il buffer:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Funzioni per lavorare con MSBufferizer

Creazione di una nuova istanza del buffer:

MSBufferizer * ms_bufferizer_new(void);

La memoria è allocata, inizializzata in ms_bufferizer_init() e viene restituito un puntatore.

Funzione di inizializzazione:

void ms_bufferizer_init(MSBufferizer *obj); 

La coda è in fase di inizializzazione q, campo Taglia è impostato su zero.

Aggiunta di un messaggio:

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

Il messaggio m viene aggiunto alla coda. Viene aggiunta la dimensione calcolata dei blocchi di dati Taglia.

Trasferimento di tutti i messaggi dalla coda dati del collegamento al buffer q:

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

Trasferimento di messaggi da un collegamento q nel buffer viene eseguita utilizzando 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 è inferiore a quella richiesta (datalen), la funzione restituisce zero, i dati non vengono copiati in data. Altrimenti, viene eseguita la copia sequenziale dei dati dalle tuple posizionate nel buffer. Dopo la copia, la tupla viene cancellata e la memoria viene liberata. La copia termina nel momento in cui vengono copiati i byte dei dati. Se nel mezzo di un blocco dati si esaurisce lo spazio, in questo messaggio il blocco dati verrà ridotto alla parte rimanente non copiata. La prossima volta che chiamerai, la copia continuerà da questo punto.

Lettura della quantità di dati attualmente disponibile nel buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Restituisce il campo Taglia buffer.

Scartare parte dei dati nel buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Il numero specificato di byte di dati viene recuperato ed eliminato. I dati più vecchi vengono scartati.

Eliminazione di tutti i messaggi nel buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Il contatore dei dati viene azzerato.

Eliminazione di tutti i messaggi nel buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Il contatore non viene azzerato.

Rimozione 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 media streamer. Ad esempio, nel filtro MS_L16_ENC, che riorganizza i byte negli esempi dall'ordine di rete all'ordine host: l16.c

Nel prossimo articolo esamineremo la questione della stima del carico del ticker e come gestire un carico di elaborazione eccessivo nello streamer multimediale.

Fonte: habr.com

Aggiungi un commento