Proučujemo VoIP motor Mediastreamer2. Deo 11

Materijal u članku preuzet je sa mog zen kanala.

Proučujemo VoIP motor Mediastreamer2. Deo 11

Mehanizam prenosa podataka

  • Blok podataka dblk_t
  • Poruka mblk_t
  • Funkcije rada sa porukama mblk_t
  • Red queue_t
  • Funkcije rada sa redovima queue_t
  • Povezivanje filtara
  • Signala tačka dijagrama obrade podataka
  • Zakulisna aktivnost tikera
  • Bafer (MSBufferizer)
  • Funkcije rada sa MSBufferizer

V prejšnjem članku Razvili smo svoj vlastiti filtar. Ovaj članak ćemo posvetiti uređaju unutrašnjeg mehanizma prenosa podataka između filtara mediastreamera. To će nam omogućiti da kasnije pišemo složene filtre uz manje truda.

Mehanizam prenosa podataka

Prenos podataka u mediastreameru vrši se pomoću redova opisanih strukturom queue_t. Kroz redove se prenose sekvence poruka tipa mblk_t, koje same po sebi ne sadrže podatke signala, već samo reference na prethodnu, sledeću poruku i na blok podataka. Pored toga, želim posebno naglasiti da postoji još jedan pokazatelj za referenciranje poruka istog tipa, što omogućava organizovanje jednostruko povezanih lista poruka. Grupu poruka ujedinjenih takvim putem nazivaćemo tuplom. Tako, svaka stavka u redu može biti pojedinačna poruka mblk_t, ili može biti i glava tupla poruka mblk_t. Svaka poruka tupla može imati svoj podračun blok podataka. Zašto su tuple potrebne, razgovaraćemo malo kasnije.

Kao što je ranije rečeno, sama poruka ne sadrži blok podataka, umesto toga sadrži samo pokazivač na prostor u memoriji gde se čuva blok. U ovoj fazi celokupna slika rada mediastreamera podseća na skladište vrata iz crtanog filma "Korporacija čudovišta", gde vrata (reference na podatke – sobe) sa ludom brzinom prolaze kroz viseće trake, dok same sobe ostaju nepomične.

Sada, krećući se kroz hijerarhiju od dna ka vrhu, detaljno ćemo razmotriti navedene entitete mehanizma prenosa podataka u mediastreameru.

Blok podataka dblk_t

Blok podataka se sastoji od zaglavlja i bafera podataka. Zaglavlje je opisano sledećom strukturom,

typedef struct datab
{
unsigned char *db_base; // Pokazivač na početak bafera podataka.
unsigned char *db_lim;  // Pokazivač na kraj bafera podataka.
void (*db_freefn)(void*); // Funkcija za oslobađanje memorije pri brisanju bloka.
int db_ref; // Brojač referenci.
} dblk_t;

Polja strukture vsebujejo kazalce na začetek in konec pomnilnika, funkcijo za brisanje podatkovnega pomnilnika. Zadnji element v glavi db_ref — števec sklicev, ko doseže nič, to služi kot signal za brisanje tega bloka iz pomnilnika. Če je blok podatkov ustvarjen s funkcijo datab_alloc() , bo podatkovni pomnilnik razmestjen v pomnilniku takoj za glavo. V vseh drugih primerih je pomnilnik lahko ločeno. V podatkovnem pomnilniku bodo shranjeni signali ali drugi podatki, ki jih želimo obdelati s filtri.

Nov primer podatkovnega bloka se ustvari s pomočjo funkcije:

dblk_t *datab_alloc(int size);

Kot vhodni parameter ji je predana velikost podatkov, ki jih bo shranjeval blok. Pomnilnik se dodeli več, da se na začetku dodeljenega pomnilnika postavi glava — struktura datab. Vendar pa pri uporabi drugih funkcij to ni vedno tako, v nekaterih primerih je lahko podatkovni pomnilnik ločen od glave podatkovnega bloka. Polja strukture so pri ustvarjanju nastavljena tako, da njeno polje db_base kaže na začetek podatkovnega področja, medtem ko db_lim kaže na njen konec. Števec sklicev db_ref se nastavi na enoto. Kazalec funkcije za čiščenje podatkov se nastavi na nič.

Sporočilo mblk_t

Kot omenjeno, elementi vrste imajo tip mblk_t, je definiran tako:

typedef struct msgb
{
  struct msgb *b_prev;   // Kazalec na prejšnji element seznama.
  struct msgb *b_next;   // Kazalec na naslednji element seznama.
  struct msgb *b_cont;   // Kazalec za dodajanje drugih sporočil k sporočilu, za ustvarjanje paketa sporočil.
  struct datab *b_datap; // Kazalec na strukturo podatkovnega bloka.
  unsigned char *b_rptr; // Kazalec na začetek podatkovnega področja za branje podatkov iz pomnilnika b_datap.
  unsigned char *b_wptr; // Kazalec na začetek podatkovnega področja za pisanje podatkov v pomnilnik b_datap.
  uint32_t reserved1;    // Rezervirano polje1, medijski streamer tja postavi upravno informacijo. 
  uint32_t reserved2;    // Rezervirano polje2, medijski streamer tja postavi upravno informacijo.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktura mblk_t na začetku vsebuje kazalce b_prev, b_next, ki so potrebni za organizacijo dvostranskega seznama (ki je vrsta queue_t).

Nato sledi kazalec b_cont, ki se uporablja le, ko sporočilo vstopi v tupl. Za zadnje sporočilo v tuplu ostane ta kazalec nič.

Nato vidimo kazalec na blok podatkov b_datap, zaradi katerega sporočilo sploh obstaja. Za njim sledijo kazalci na območje znotraj podatkovnega bloka. Polje b_rptr kazalec označuje mesto, s katerega se bodo brali podatki iz predpomnilnika. Polje b_wptr označuje mesto, s katerega se bodo izvajale zapise v predpomnilnik.

Preostala polja so rezervirana in niso povezana z delovanjem mehanizma prenosa podatkov.

Spodaj je prikazano posamezno sporočilo z imenom m1 in podatkovnim blokom d1.
Proučujemo VoIP motor Mediastreamer2. Deo 11
Na naslednji sliki je prikazan tupl iz treh sporočil m1, m1_1, m1_2.
Proučujemo VoIP motor Mediastreamer2. Deo 11

Funkcije za delo s sporočili mblk_t

Novo sporočilo mblk_t ustvari funkcija:

mblk_t *allocb(int size, int pri); 

ta alocira novo sporočilo v pomnilniku mblk_t s podatkovnim blokom določene velikosti size, drugi argument — pri se v obravnavani različici knjižnice ne uporablja. Ostati mora ničelno. Med delovanjem funkcije bo pomnilnik dodeljen za strukturo novega sporočila in klicana bo funkcija mblk_init(), ki bo vse polja ustvarjene instance strukture nastavila na nič in nato, s pomočjo prej omenjenega, datab_alloc(), ustvarila podatkovni predpomnilnik. Nato bo izvedena nastavitev polj v strukturi:

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

Na izhodu dobimo novo sporočilo z inicializiranimi polji in praznim podatkovnim predpomnilnikom. Da dodamo podatke v sporočilo, je treba te kopirati v predpomnilnik podatkovnega bloka:

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

kjer data je kazalec na vir podatkov, medtem ko size je njihova velikost.
Nato je treba posodobiti kazalec na zapisno mesto, da ponovno kaže na začetek prostega območja v predpomnilniku:

msg->b_wptr = msg->b_wptr + size

Če je potrebno ustvariti sporočilo iz že obstoječega predpomnilnika, brez kopiranja, se za to uporabi funkcija:

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

Funkcija po ustvarjanju sporočila in strukture podatkovnega bloka nastavi njene kazalce na podatke na naslovu buf. T.j. v tem primeru podatkovni predpomnilnik ni nameščen za polji glave podatkovnega bloka, kot je bilo pri ustvarjanju podatkovnega bloka s funkcijo. datab_alloc()Преданная функции буфер с данными останется на месте, но с помощью указателей будет перенаправлен к только что созданному заголовку блока данных, а тот соответственно к сообщению.

К одному сообщению mblk_t могут быть последовательно прикреплены несколько блоков данных. Это делается функцией:

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

mp — сообщение, к которому будет добавлен еще один блок данных;
data — указатель на блок, копия которого будет добавлена в сообщение;
size — размер данных;
pad — флаг того, что размер выделяемой памяти должен быть выровнен по границе 4 байт (дополнение будет выполнено нулями).

Если в имеющемся буфере данных сообщения достаточно места, то новые данные будут добавлены за уже имеющимися там данными. Если свободного места в буфере данных сообщения окажется меньше чем size, то создается новое сообщение с достаточным размером буфера, и данные копируются в его буфер. Это новое сообщение соединяется с исходным с помощью указателя b_cont. В этом случае сообщение превращается в кортеж.

Если в кортеж требуется добавить еще один блок данных, то нужно использовать функцию:

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

она найдет последнее сообщение в кортеже (у него b_cont будет нулевым) и вызовет для этого сообщения функцию appendb().

Узнать размер данных в сообщении или кортеже можно с помощью функции:

int msgdsize(const mblk_t *mp);

она пробежится по всем сообщениям кортежа и вернет общее количество данных в буферах данных этих сообщений. Для каждого сообщения количество данных вычисляется так:

 mp->b_wptr - mp->b_rptr

Чтобы объединить два кортежа применяется функция:

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

она присоединяет кортеж newm в конец кортежа mp и возвращает указатель на последнее сообщение получившегося кортежа.

При необходимости, кортеж можно превратить в одно сообщение с единым блоком данных, это делается функцией:

void msgpullup(mblk_t *mp,int len);

если аргумент len равен -1, то размер выделяемого буфера определяется автоматически. Если len Če je število pozitivno, bo ustvarjen predpomnilnik te velikosti, v katerega bodo kopirani podatki tupla. Če se predpomnilnik napolni, se kopiranje prekine. Prvo sporočilo tupla bo prejelo predpomnilnik nove velikosti s kopiranimi podatki. Ostala sporočila bodo izbrisana, spomin pa vrnjen v kup.

Ob izbrisu strukture mblk_t se upošteva števec referenc podatkovnega bloka, če ob klicu freeb() doseže nič, se predpomnilnik podatkov izbriše skupaj z instanco mblk_t, na katero kaže.

Inicializacija polj novega sporočila:

void mblk_init(mblk_t *mp);

Dodajanje še enega niza podatkov v sporočilo:

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

Če novi podatki ne ustrezajo prostornini prostega prostora v predpomnilniku sporočila, se sporočilu priloži ločeno ustvarjeno sporočilo s potrebnim velikostnim predpomnilnikom (v prvem sporočilu se nastavi kazalec na dodano sporočilo) in sporočilo se spremeni v tuple.

Dodajanje niza podatkov v tuple:

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

Funkcija klicanje appendb() izvede v zanki.

Združevanje dveh tuple v eno:

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

Sporočilo newm bo pripeto na mp.

Ustvarjanje kopije posameznega sporočila:

mblk_t *copyb(const mblk_t *mp);

Popolno kopiranje tupla z vsemi podatkovnimi bloki:

mblk_t *copymsg(const mblk_t *mp);

Elementi tupla se kopirajo s funkcijo copyb().

Ustvarjanje lahke kopije sporočila. mblk_tPri tem se podatkovni blok ne kopira, temveč se poveča števec njegovih referenc. db_ref:

mblk_t *dupb(mblk_t *mp);

Ustvarjanje lahke kopije tupla. Podatkovni bloki se ne kopirajo, le njihovi števci referenc se povečajo. db_ref:

mblk_t *dupmsg(mblk_t* m);

Zlepljenje vseh sporočil tupla v eno sporočilo:

void msgpullup(mblk_t *mp,size_t len);

Če je argument len enak -1, se velikost dodeljenega predpomnilnika določi samodejno.

Izbris sporočila, tupla:

void freemsg(mblk_t *mp);

Števec referenc podatkovnega bloka se zmanjša za enoto. Če ob tem doseže nič, se tudi podatkovni blok izbriše.

Štetje skupne prostornine podatkov v sporočilu ali tuple.

size_t msgdsize(const mblk_t *mp);

Pridobitev sporočila iz repa vrste:

mblk_t *ms_queue_peek_last (q);

Kopiranje vsebine rezerviranih polj enega sporočila v drugo sporočilo (v teh poljih so v resnici zastavice, ki jih uporablja medijski strežnik):

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

Vrsta queue_t

Queuing messages in the media streamer is implemented as a circular doubly linked list. Each list element contains a pointer to a data block with signal readings. This means that only the pointers to the data blocks move in the queue, while the actual data remains stationary. In other words, only the references to them are moved.
The structure describing the queue queue_t, is shown below:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Empty" element of the queue, does not point to data, used only for managing the queue. When initializing the queue (qinit()), its pointers are set to point to itself. */
   int q_mcount;        // Number of elements in the queue.
} queue_t;

The structure contains the field — a pointer _q_stopper of type *mblk_t, which points to the first element (message) in the queue. The second field of the structure is a counter of messages that are in the queue.
The image below shows the queue named q1, containing 4 messages m1, m2, m3, m4.
Proučujemo VoIP motor Mediastreamer2. Deo 11
The next image shows the queue named q1, containing 4 messages m1, m2, m3, m4. The message m2 is the head of the tuple, to which two more messages m2_1 and m2_2 are linked.

Proučujemo VoIP motor Mediastreamer2. Deo 11

Funkcije rada sa redovima queue_t

Initialization of the queue:

void qinit(queue_t *q);

Field _q_stopper (hereafter referred to as "stopper") is initialized by the function mblk_init(), its pointer to the previous element and the next element are set to point to itself. The element counter of the queue is reset to zero.

Adding a new element (message):

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

The new element m is added to the end of the list, the pointers of the element are set so that the stopper becomes the next element for it, and it becomes the previous element for the stopper. The element counter of the queue is incremented.

Extracting an element from the queue:

mblk_t * getq(queue_t *q); 

the message that is after the stopper is extracted, the element counter is decremented. If there are no elements in the queue other than the stopper, 0 is returned.

Inserting a message into the queue:

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

Element mp is inserted before the element emp. Если emp=0, then the message is added to the tail of the queue.

Extracting a message from the head of the queue:

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

The element counter is decremented.

Reading the pointer to the first element in the queue:

mblk_t * peekq(queue_t *q); 

Removing all elements from the queue while deleting the actual elements:

void flushq(queue_t *q, int how);

Argument how ni se uporablja. Število elementov v čakalni vrsti se nastavi na nič.

Makar za branje kazalca na zadnji element čakalne vrste:

mblk_t * qlast(queue_t *q);

Pri delu z sporočilnimi čakalnimi vrstami je treba upoštevati, da ob klicu ms_queue_put(q, m) z ničelnim kazalcem na sporočilo, funkcija zaide v neskončno zanko. Vaš program se bo ustavil. Podobno se obnaša ms_queue_next(q, m).

Povezivanje filtara

Omenjena čakalna vrsta se uporablja za prenos sporočil od enega filtra do drugega ali od enega do več filtrov. Filtri in njihova povezava med seboj tvorijo usmerjen graf. Vhod ali izhod filtra bomo imenovali s splošnim izrazom 'pin'. Za opis zaporedja povezav med filtri se v medijskem strežniku uporablja pojem 'signalna točka'. Signalna točka je struktura _MSCPoint, ki vsebuje kazalec na filter in številko enega od njegovih pinov, s tem opisuje povezavo enega od vhodov ali izhodov filtra.

Signala tačka dijagrama obrade podataka

typedef struct _MSCPoint{
struct _MSFilter *filter; // Kazalec na filter medijskega strežnika.
int pin;                        // Številka enega od vhodov ali izhodov filtra, tj. pin.
} MSCPoint;

Pini filtrov so oštevilčeni, pri čemer se začne štetje z nič.

Povezava dveh pinov prek sporočilne čakalne vrste je opisana s strukturo _MSQueue, ki vsebuje sporočilno čakalno vrsto in kazalce na dve signalni točki, ki jih povezuje:

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

To strukturo bomo imenovali signalna povezava. Vsak filter medijskega strežnika vsebuje tabelo vhodnih povezav in tabelo izhodnih povezav (MSQueue). Velikost tabel se nastavi ob ustvarjanju filtra, kar smo že storili s pomočjo izvoznega spremenljivke tipa MSFilterDesc, ko smo razvijali naš lasten filter. Spodaj je prikazana struktura, ki opisuje vsak filter v medijskem strežniku, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Pointer to the filter descriptor. */
    /* Protected attributes, they cannot be shifted or removed as this would disrupt plugin operation. */
    ms_mutex_t lock;      /* Semaphore. */
    MSQueue **inputs;     /* Table of input links. */
    MSQueue **outputs;    /* Table of output links. */
    struct _MSFactory *factory; /* Pointer to the factory that created this filter instance. */
    void *padding;              /* Not used, will be utilized if protected fields are added. */
    void *data;                 /* Pointer to an arbitrary structure for storing the filter's internal state data and intermediate computations. */
    struct _MSTicker *ticker;   /* Pointer to the ticker object, which must not be null when the process() function is called. */
    /* private attributes, they can be moved and changed at any time */
    MSList *notify_callbacks; /* List of callbacks used for handling filter events. */
    uint32_t last_tick;       /* The number of the last tick when the process() call was executed. */
    MSFilterStats *stats;     /* Filter operation statistics. */
    int postponed_task; /* Number of postponed tasks. Some filters may delay data processing (the process() call) for several ticks. */
    bool_t seen;  /* A flag used by the ticker to indicate that this filter instance has already been serviced in the current tick. */
};
typedef struct _MSFilter MSFilter;

After connecting the filters in our C program according to our design (but without connecting the ticker), we have thus created a directed graph, where the nodes are instances of the structure MSFilter, and the edges are instances of links MSQueue.

Zakulisna aktivnost tikera

When I told you that the ticker is a filter that outputs ticks, it wasn't the whole truth about it. The ticker is an object that triggers function executions on a timed basis process() for all the filters in the schema (graph) it is connected to. When we connect the ticker to a filter in the graph within our C program, we indicate to the ticker the graph it will control from that moment until we disconnect it. After connecting, the ticker starts to inspect the entrusted graph, forming a list of filters that are part of it. To avoid 'counting' the same filter twice, it marks discovered filters by setting a flag seen. The search is conducted through the link tables that each filter has.

Med svojemu uvodnem izletu po grafu ticker preveri, ali je med filtri vsaj en, ki deluje kot vir podatkovnih blokov. Če taki filtri ne obstajajo, se graf šteje za napačnega in ticker nujno zaključi delo.

Če je graf "pravilen", se za vsak najdeni filter ob inicializaciji pokliče funkcija preprocess(). Ko pride čas za naslednji cikel obdelave (privzeto vsakih 10 milisekund), ticker pokliče funkcijo process() za vse prej najdene vire filtriranja, nato pa tudi za ostale filtre na seznamu. Če filter ima vhodne povezave, se zagon funkcije process() ponavlja, dokler se čakalne vrste vhodnih povezav ne izpraznijo. Nato se preide na naslednji filter na seznamu in "prekolesari" ga, dokler se vhodne povezave ne osvobodijo sporočil. Ticker prehaja od filtra do filtra, dokler seznam ni končan. Na tem mestu se cikel obdelave konča.

Zdaj se vrnimo k naborom in pogovorimo se o tem, zakaj je bila v medijskem pretvorniku dodana taka entiteta. Na splošno se prostornina podatkov, potrebna algoritmu, ki deluje znotraj filtra, ne ujema in ni deljiva z velikostjo podatkovnih buferjev, ki prihajajo na vhodu. Na primer, pišemo filter, ki izvaja hitro Fourierjevo transformacijo, ki po definiciji lahko obdela le bloke podatkov, katerih velikost je enaka potencam dvojke. Naj bo to 512 vzorcev. Če podatki izvirajo iz telefonskega kanala, bo podatkovni bufer vsakega sporočila na vhodu prinesel po 160 vzorcev signala. Obstaja skušnjava, da ne vzamemo podatkov z vhodu, dokler ne pride do potrebne količine podatkov. A v tem primeru pride do kolizije s tickerjem, ki bo neuspešno poskušal preklopiti filter, dokler se vhodna povezava ne izprazni. Prej smo to pravilo označili kot tretji princip delovanja filtra. V skladu s tem načelom mora funkcija process() filtra pobrati vse podatke iz vhodnih čakalnih vrst.

Poleg tega ni mogoče prevzeti le 512 podatkovnih točk, saj jih je mogoče prevzeti le v celotnih blokih, tj. filtrirati bo potrebno 640 podatkovnih točk in uporabiti 512 od njih, preostanek pa do zbiranja nove serije podatkov. Tako mora naš filter, poleg svojega glavnega dela, zagotavljati tudi pomožne naloge za medsebojno shranjevanje vhodnih podatkov. Razvijalci medijskega pretvornika so za reševanje te skupne naloge razvili poseben objekt - MSBufferizer (buffer), ki to nalogo rešuje s pomočjo tuplov.

Bafer (MSBufferizer)

To je objekt, ki bo shranjeval vhodne podatke znotraj filtra in jih začel oddajati v obdelavo, takoj ko bo količina informacij zadostna za izvajanje filtra. Medtem ko buffer kopičijo podatke, bo filter deloval v praznem načinu, brez porabe računalniške moči procesorja. Toda takoj ko funkcija branja iz bufferja vrne vrednost, ki ni nič, funkcija process() filtra začne prevzemati in obdelovati podatke iz bufferja v obrokih ustrezne velikosti, dokler ne zmanjka.
Nepotrebni podatki ostajajo v bufferju kot prvi element tupla, ki mu sledijo naslednji bloki vhodnih podatkov.

Struktura, ki opisuje buffer:

struct _MSBufferizer{
queue_t q; /* Čakalna vrsta sporočil. */
int size; /* Skupna velikost podatkov, ki so trenutno v bufferju. */
};
typedef struct _MSBufferizer MSBufferizer;

Funkcije rada sa MSBufferizer

Ustvarjanje novega primera bufferja:

MSBufferizer * ms_bufferizer_new(void);

Dodeli se pomnilnik, inicializira se v ms_bufferizer_init() in vrne se kazalec.

Funkcija inicializacije:

void ms_bufferizer_init(MSBufferizer *obj); 

Inicializira se čakalna vrsta q, polje size se nastavi na nič.

Dodajanje sporočila:

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

Sporočilo m se doda v čakalno vrsto. Izračunana velikost podatkovnih blokov se doda k size.

Preklop bufferja vseh sporočil čakalne vrste podatkovne povezave q:

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

Prenos sporočil iz povezave q v buffer se izvede s pomočjo funkcije ms_bufferizer_put().

Branje iz bufferja:

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

Če je velikost shranjenih podatkov v bufferju manjša od zahtevane (datalen), to funkcija vrne ničlo, kopiranje podatkov v data se ne izvede. V nasprotnem primeru se podatki zaporedno kopirajo iz tuple, ki so v predpomnilniku. Po kopiranju se tuple odstrani in spomin se osvobodi. Kopiranje se zaključi, ko je skopiranih datalen bajtov. Če se prostor konča sredi podatkovnega bloka, se v tem sporočilu podatkovni blok skrajša na preostali nekopirani del. Pri naslednjem klicu se kopiranje nadaljuje od te točke.

Preberite količino podatkov, ki so trenutno na voljo v predpomnilniku:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Vrne polje size predpomnilnika.

Prekrivanje dela podatkov, ki so v predpomnilniku:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Določeno število bajtov podatkov se izvleče in preloži. Preloženi so najstarejši podatki.

Odstranitev vseh sporočil, ki so v predpomnilniku:

void ms_bufferizer_flush(MSBufferizer *obj); 

Števec podatkov se ponastavi na nič.

Odstranitev vseh sporočil, ki so v predpomnilniku:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Ponastavitev števca se ne izvede.

Odstranitev predpomnilnika in osvoboditev spomina:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Primeri uporabe predpomnilnika so na voljo v izvorni kodi več filtrov medijskega pretakalnika. Na primer pri filtru MS_L16_ENC, ki izvaja preureditev bajtov v vzorcih iz omrežnega reda v red gostitelja: l16.c

V naslednjem članku bomo obravnavali vprašanje ocene obremenitve na tickerju in načine za obvladovanje prekomerne računalniške obremenitve v medijskem pretakalniku.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster