Vi undersøger VoIP-motoren Mediastreamer2. Del 11

Materialet i artiklen er taget fra min zen-kanal.

Vi undersøger VoIP-motoren Mediastreamer2. Del 11

Data Movement Mechanism

  • Data Block dblk_t
  • Message mblk_t
  • Functions for Working with Messages mblk_t
  • Queue queue_t
  • Functions for Working with Queues queue_t
  • Connection of Filters
  • Signal Point of the Data Processing Graph
  • Backstage Activities of the Ticker
  • Bufferizer (MSBufferizer)
  • Functions for Working with MSBufferizer

I den forrige artikel We developed our own filter. This article will be dedicated to the internal structure of data movement mechanisms between media-streaming filters. This will allow us to write sophisticated filters with less effort in the future.

Data Movement Mechanism

Data movement in the media streamer is carried out using queues described by the structure queue_t. Messages of type mblk_tmove through the queues, which themselves do not contain signal data, but only references to the previous, next message, and to the data block. Additionally, I want to particularly emphasize that there is a field for referencing a message of the same type, which allows for the organization of a singly linked list of messages. A group of messages connected in this way will be called a tuple. Thus, any element of the queue can be a single message mblk_t, or it might be the head of a message tuple mblk_t. Each message in a tuple can have its own associated data block. We will discuss the purpose of tuples a bit later.

As mentioned above, the message itself does not contain a data block; instead, it only contains a pointer to the memory area where the block is stored. In this part, the overall picture of the media streamer's operation resembles a warehouse of doors in the animated film 'Monsters, Inc.', where doors (links to data — rooms) move at a crazy speed along overhead conveyors, while the rooms themselves remain stationary.

Now, moving up the hierarchy from bottom to top, let’s examine in detail the listed entities of the data transmission mechanism in the media streamer.

Data Block dblk_t

The data block consists of a header and a data buffer. The header is described by the following structure,

typedef struct datab
{
unsigned char *db_base; // Pointer to the start of the data buffer.
unsigned char *db_lim;  // Pointer to the end of the data buffer.
void (*db_freefn)(void*); // Memory freeing function when deleting the block.
int db_ref; // Reference counter.
} dblk_t;

Strukturfelterne indeholder henvisninger til begyndelsen af bufferet, slutningen af bufferet og en funktion til at slette data-bufferet. Det sidste element i overskriften db_ref — referencetæller; når den når nul, signalerer det, at denne blok kan fjernes fra hukommelsen. Hvis datablokken er oprettet med funktionen datab_alloc() , vil databufferet blive placeret i hukommelsen lige efter overskriften. I alle andre tilfælde kan bufferet være placeret et andet sted. I databufferet vil der være signalmålinger eller andre data, som vi ønsker at behandle med filtre.

En ny instans af datablock oprettes med funktionen:

dblk_t *datab_alloc(int size);

Som inputparameter modtager den størrelsen på de data, som blokken skal indeholde. Mere hukommelse tildeles for at placere overskriften — strukturen datab. Men ved brug af andre funktioner sker dette ikke altid; i nogle tilfælde kan databufferet være placeret separat fra datablockens overskrift. Strukturfelterne indstilles ved oprettelse, så dens felt db_base peger på begyndelsen af datarammeområdet, og db_lim peger på dens slutning. Referencetælleren db_ref sættes til en. Henvisningen til dataoprydningsfunktionen sættes til nul.

Besked mblk_t

Som nævnt har køelementerne typen mblk_t, der er defineret som følger:

typedef struct msgb
{
  struct msgb *b_prev;   // Henvisning til det forrige element i listen.
  struct msgb *b_next;   // Henvisning til det næste element i listen.
  struct msgb *b_cont;   // Henvisning til at forbinde andre meddelelser til denne meddelelse for at skabe en meddelelseskortlægning.
  struct datab *b_datap; // Henvisning til strukturen for datablock.
  unsigned char *b_rptr; // Henvisning til begyndelsen af dataområdet for at læse data fra b_datap.
  unsigned char *b_wptr; // Henvisning til begyndelsen af dataområdet for at skrive data til b_datap.
  uint32_t reserved1;    // Reserveret felt1; medie-streameren placerer tjenesteoplysninger her.
  uint32_t reserved2;    // Reserveret felt2; medie-streameren placerer tjenesteoplysninger her.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktur mblk_t indeholder først henvisningerne b_prev, b_next, som er nødvendige for at organisere en dobbelt-kædede liste (som køen er. queue_t).

Derefter kommer henvisningen b_cont, som kun anvendes, når beskeden er i tuplet. For den sidste besked i tuplet forbliver denne peger nul.

Dernæst ser vi en peger til datablokken. b_datap, som er grunden til, at beskeden eksisterer. Derefter følger pegere til området inden for datablokken. Feltet b_rptr angiver, hvorfra data vil blive læst fra bufferen. Feltet b_wptr angiver, hvorfra der vil blive skrevet til bufferen.

De resterende felter er af administrativ karakter og relaterer sig ikke til driften af datatransmissionsmekanismen.

Nedenfor vises en enkelt besked med navnet m1 og datablokken. d1.
Vi undersøger VoIP-motoren Mediastreamer2. Del 11
På næste billede vises et tuplet med tre beskeder. m1, m1_1, m1_2.
Vi undersøger VoIP-motoren Mediastreamer2. Del 11

Funktioner til arbejde med beskeder. mblk_t

En ny besked mblk_t oprettes af funktionen:

mblk_t *allocb(int size, int pri); 

den placerer en ny besked i hukommelsen mblk_t med en datablok af angivet størrelse. størrelse, det andet argument — pri bruges ikke i den version af biblioteket, der betragtes. Det skal forblive nul. I løbet af funktionen vil der blive tildelt hukommelse til strukturen for den nye besked, og funktionen mblk_init(), som nulstiller alle felter i den oprettede struktur, vil derefter blive kaldt og ved hjælp af ovennævnte datab_alloc(), oprette databufferen. Derefter vil felterne i strukturen blive konfigureret:

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

Udgangen er en ny besked med initialiserede felter og en tom datbuffer. For at tilføje data til beskeden skal de kopieres til datablokkens buffer:

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

hvor data — peger på datakilden, og størrelse — deres størrelse.
derefter skal pegeren til skrivepunktet opdateres, så den igen peger på begyndelsen af det frie område i bufferen:

msg->b_wptr = msg->b_wptr + size

Hvis det er nødvendigt at oprette en besked fra allerede eksisterende buffer, uden kopiering, så bruges funktionen:

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

Funktionen vil efter oprettelsen af beskeden og datablokstrukturen indstille dens pegere til data ved adressen buf. Dvs. i dette tilfælde er databufferen ikke placeret umiddelbart efter felterne i datablokheaderen, som det var tilfældet ved oprettelsen af datablocken af funktionen. datab_alloc()Den overførte buffer med data vil forblive, hvor den var, men ved hjælp af pekere vil den blive skubbet til den netop oprettede datablokheader, som så til gengæld vil blive knyttet til beskeden.

Til én besked mblk_t kan flere datablokke påføres sekventielt. Dette gøres med funktionen:

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

mp — beskeden, hvortil yderligere en datablok skal tilføjes;
data — en peger til blokken, hvis kopi skal føjes til beskeden;
størrelse — størrelsen af data;
pad — flag, der angiver, at størrelsen af den tildelte hukommelse skal være justeret til en 4-bits grænse (udfyldning vil blive udført med nuller).

Hvis der er tilstrækkelig plads i det eksisterende datagrænse for beskeden, vil de nye data blive limet på de allerede eksisterende data. Hvis der er mindre ledig plads i datagrænsen for beskeden størrelse, så oprettes en ny besked med tilstrækkelig bufferstørrelse, og data kopieres til dens buffer. Denne nye besked knyttes til den oprindelige via en peger b_cont. I dette tilfælde omdannes beskeden til et tuple.

Hvis der kræves at tilføje endnu en datablok til tuplen, skal funktionen anvendes:

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

den vil finde den seneste besked i tuplen (den vil have b_cont vil være nul) og kalde funktionen appendb().

For at finde størrelsen af data i beskeden eller tuplen kan følgende funktion anvendes:

int msgdsize(const mblk_t *mp);

den vil gennemgå alle beskederne i tuplen og returnere det samlede antal data i data buffere af disse beskeder. For hver besked beregnes mængden af data således:

 mp->b_wptr - mp->b_rptr

For at fusionere to tupler anvendes funktionen:

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

den tilføjer tuplen newm til slutningen af tuplen mp og returnerer en peger til den sidst oprettede besked i den resulterende tuple.

Om nødvendigt kan tuplen omdannes til en enkelt besked med én datablok, hvilket gøres med funktionen:

void msgpullup(mblk_t *mp,int len);

hvis argumentet len er lig med -1, så bestemmes størrelsen af den tildelte buffer automatisk. Hvis len Et positivt tal vil resultere i, at der oprettes en buffer af denne størrelse, hvori data fra tuplebeskederne kopieres. Hvis bufferen løber tør, stoppes kopieringen her. Den første besked i tuplen vil få en buffer af den nye størrelse med de kopierede data. De øvrige beskeder vil blive slettet, og hukommelsen vil blive returneret til bunken.

Ved sletning af strukturen mblk_t tages blockdatas referencetæller i betragtning, hvis ved kaldet freeb() den viser sig at være lig med nul, slettes databufferen sammen med instansen, mblk_tsom den peger på.

Initialisering af felterne i den nye besked:

void mblk_init(mblk_t *mp);

Tilføjelse af yderligere data til beskeden:

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

Hvis de nye data ikke kan være i den frie plads i databufferen af beskeden, tilknyttes en separat oprettet besked til beskeden med den nødvendige bufferstørrelse (i den første besked indstilles en peger til den tilføjede besked) og beskeden bliver til en tuple.

Tilføjelse af en dataportion til tuplen:

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

Funktionen kalder appendb() i en løkke.

Sammenfletning af to tupler til én:

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

Besked newm vil blive vedhæftet til mp.

Oprettelse af en kopi af en enkelt besked:

mblk_t *copyb(const mblk_t *mp);

Fuld kopi af tuplen med alle datablocke:

mblk_t *copymsg(const mblk_t *mp);

Tupleelementer kopieres af funktionen copyb().

Oprettelse af en let kopi af beskeden mblk_t. Her kopieres datablocken ikke, men referencetælleren for den øges db_ref:

mblk_t *dupb(mblk_t *mp);

Oprettelse af en let kopi af tuplen. Datablocke kopieres ikke, kun deres referencetællere øges. db_ref:

mblk_t *dupmsg(mblk_t* m);

Sammenføjning af alle beskeder i tuplen til én besked:

void msgpullup(mblk_t *mp,size_t len);

Hvis argumentet len er lig med -1, bestemmes størrelsen af den tildelte buffer automatisk.

Sletning af besked, tuplen:

void freemsg(mblk_t *mp);

Referencetælleren for datablocken reduceres med én. Hvis den når nul, slettes datablocken også.

Tælling af den samlede mængde data i beskeden eller tuplen.

size_t msgdsize(const mblk_t *mp);

Udtrækning af besked fra enden af køen:

mblk_t *ms_queue_peek_last (q);

Kopiering af indholdet af de reserverede felter fra én besked til en anden besked (i disse felter findes i virkeligheden de flag, der bruges af mediestreameren):

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

Kø queue_t

Køen til meddelelsesstrømmen er implementeret som en cirkulær dobbeltkæde. Hvert element i kæden indeholder en pegepind til et datablok med signalmålinger. Det betyder, at kun pegepindene til datablokke bevæger sig i rækkefølge, mens selve dataene forbliver stationære. Dvs. kun henvisningerne til dem bevæger sig.
Strukturen, der beskriver køen queue_t, vises nedenfor:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Dummy" køelement, peger ikke på data, bruges kun til at styre køen. Ved initialisering af køen (qinit()) tilpasses dens pegepinde, så de peger på sig selv. */
   int q_mcount;        // Antallet af elementer i køen.
} queue_t;

Strukturen indeholder et felt — en pegepind _q_stopper af typen *mblk_t, som peger på det første element (meddelelsen) i køen. Det andet felt i strukturen er en tæller for de meddelelser, der er i køen.
På billedet nedenfor vises køen med navnet q1, som indeholder 4 meddelelser m1, m2, m3, m4.
Vi undersøger VoIP-motoren Mediastreamer2. Del 11
Det næste billede viser køen med navnet q1, som indeholder 4 meddelelser m1, m2, m3, m4. Meddelelsen m2 er hovedet af tuplen, der indeholder yderligere to meddelelser m2_1 og m2_2.

Vi undersøger VoIP-motoren Mediastreamer2. Del 11

Functions for Working with Queues queue_t

Initialiseringen af køen:

void qinit(queue_t *q);

Felt _q_stopper (her kaldet "stopper") initialiseres med funktionen mblk_init(), dens pegepinde til det forrige og det næste element tilpasses, så de peger på den selv. Tælleren for elementer i køen nulstilles.

Tilføjelse af et nyt element (meddelelse):

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

Det nye element m tilføjes til slutningen af listen, pegepindene for elementet tilpasses, så stopperen bliver det næste element, og det for stopperen bliver det forrige. Tælleren for antallet af elementer i køen inkrementeres.

Uddragning af et element fra køen:

mblk_t * getq(queue_t *q); 

den meddelelse, der står efter stopperen, hentes, og tælleren for elementer dekrementeres. Hvis der ikke er andre elementer i køen end stopperen, returneres 0.

Indsættelse af en meddelelse i køen:

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

Elementet mp indsættes før elementet emp. Hvis emp=0, så tilføjes meddelelsen til bunden af køen.

Uddragning af en meddelelse fra hovedet af køen:

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

Tælleren for elementer dekrementeres.

Læsning af pegepinden til det første element i køen:

mblk_t * peekq(queue_t *q); 

Sletning af alle elementer fra køen med sletning af selve elementerne:

void flushq(queue_t *q, int how);

Argumentet how ikke i brug. Kølement tælleren sættes til nul.

Makro til at læse pegeren på det sidste element i køen:

mblk_t * qlast(queue_t *q);

Når man arbejder med meddelelseskøer, skal man være opmærksom på, at ved kald af ms_queue_put(q, m) med en nulpeger på meddelelsen, vil funktionen gå i loop. Dit program vil fryse. Ligeledes opfører sig ms_queue_next(q, m).

Connection of Filters

Den tidligere beskrevne kø anvendes til at overføre meddelelser fra et filter til et andet eller fra et til flere filtre. Filtrene og deres forbindelser til hinanden danner en rettet graf. Indgangen eller udgangen af et filter kaldes generelt "pin". For at beskrive rækkefølgen af forbindelserne mellem filtrene anvendes begrebet "signaleringspunkt" i mediestreameren. Et signaleringspunkt er en struktur _MSCPoint, som indeholder en peger på filteret og nummeret på en af dets pins, hvilket beskriver forbindelsen af en af indgange eller udgange fra filteret.

Signal Point of the Data Processing Graph

typedef struct _MSCPoint{
struct _MSFilter *filter; // Peger på mediestreamer filter.
int pin;                       // Nummeret på en af indgange eller udgange fra filteret, dvs. pin.
} MSCPoint;

Pins af filtrene nummereres fra nul.

Forbindelsen af to pins via en meddelelseskø beskrives af strukturen _MSQueue, som indeholder en meddelelseskø og pegere til to signaleringspunkter, som den forbinder:

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

Vi kalder denne struktur en signaleringslink. Hver mediestreamer filter indeholder en tabel med indgange links og en tabel med udgange links (MSQueue). Størrelsen på tabellerne bestemmes ved oprettelse af filteret, hvilket vi tidligere har gjort ved hjælp af den eksporterede variabel af typen MSFilterDesc, da vi udviklede vores eget filter. Nedenfor er strukturen, der beskriver ethvert filter i mediestreameren, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Peg til filterdeskriptor. */
    /* Beskyttede attributter, de kan ikke flyttes eller fjernes, ellers vil funktionen med plugins blive forstyrret. */
    ms_mutex_t lock;      /* Semaphore. */
    MSQueue **inputs;     /* Tabel over indgangslinks. */
    MSQueue **outputs;    /* Tabel over udgangslinks. */
    struct _MSFactory *factory; /* Peg til fabrikken, der oprettede denne filterinstans. */
    void *padding;              /* Bruger ikke dette, det vil blive anvendt hvis beskyttede felter tilføjes. */
    void *data;                 /* Peg til en vilkårlig struktur til opbevaring af filterets interne tilstand og mellemliggende beregninger. */
    struct _MSTicker *ticker;   /* Peg til tickerobjektet, som ikke må være nul, når funktionen process() kaldes. */
    /* private attributes, they can be moved and changed at any time */
    MSList *notify_callbacks; /* Liste over callback-funktioner, der bruges til at håndtere filterbegivenheder. */
    uint32_t last_tick;       /* Nummeret på den sidste tik, hvor process() blev kaldt. */
    MSFilterStats *stats;     /* Statistik for filterets drift. */
    int postponed_task; /* Antallet af udsatte opgaver. Nogle filtre kan forsinke databehandling (kald af process()) i flere tik. */
    bool_t seen;  /* Flag, som ticker bruger til at markere, at denne filterinstans allerede er blevet behandlet i denne tik. */
};
typedef struct _MSFilter MSFilter;

Når vi i C-programmet har forbundet filtrene i overensstemmelse med vores plan (men ikke tilsluttet tickeren), har vi skabt en rettet graf, hvor noderne er instanser af strukturen MSFilter, og kanterne er instanser af links MSQueue.

Backstage Activities of the Ticker

Da jeg sagde til dig, at tickeren er en kildefilter for takter, var det ikke hele sandheden om ham. Tickeren er et objekt, der sensorisk aktiverer funktioner process() for alle filtre i diagrammet (grafen), som det er tilsluttet. Når vi i C-programmet tilslutter tickeren til filteret i grafen, viser vi tickeren grafen, som den fra dette punkt vil styre, indtil vi frakobler den. Efter tilslutningen begynder tickeren at overvåge den tildelte graf, og sammensætter en liste over filtre, som den indeholder. For ikke at "tælle" den samme filter to gange markerer den de opdagede filtre ved at sætte et flag i dem seen. Søgningen udføres gennem de linktabeller, som hver filter har.

Under sin introduktionsturne tjekker tickeren, om der er nogen filtre, der fungerer som kilder til datablokke. Hvis der ikke findes sådanne, betragtes grafen som ugyldig, og tickeren afslutter arbejdet straks.

Hvis grafen viser sig at være 'gyldig', kaldes en funktion for hver fundet filter for at initialisere det. preprocess(). Når det er tid til den næste behandlingscyklus (som standard hver 10. millisekund), kalder tickeren funktionen process() for alle de tidligere fundne kildefiltre, og derefter for resten af filtrene på listen. Hvis filteret har indgående links, gentages opstarten af funktionen process() indtil indgående link-køer er tomme. Herefter går den videre til det næste filter på listen og 'ruller' det, indtil indgående links er ryddet for meddelelser. Tickeren bevæger sig fra filter til filter, indtil listen er udtømt. Behandlingcyklen slutter her.

Nu vender vi tilbage til tuplerne og taler om, hvorfor en sådan entitet blev tilføjet i mediestrømmeren. Generelt set stemmer datamængden, der er nødvendig for algoritmen, der kører inde i filteret, ikke overens med og er ikke en multipel af størrelsen på databufferne, der kommer ind. For eksempel, hvis vi skriver et filter, der udfører en hurtig Fourier-transformation, som per definition kun kan behandle datablokke, hvis størrelse er en potens af to. Lad os sige, at det er 512 prøver. Hvis data genereres via en telefonlinje, vil databufferet for hver indkomne meddelelse give os 160 prøver af signalet. Der er en fristelse til ikke at hente data fra indgangen, før der er den nødvendige mængde data. Men i dette tilfælde vil der opstå en konfliktsituation med tickeren, som uden held vil forsøge at rulle filteret, indtil indgående link er tømt. Tidligere har vi defineret denne regel som det tredje princip for filterets funktion. I overensstemmelse med dette princip skal funktionen process() for filteret hente alle data fra indgangskøerne.

Derudover kan du ikke hente kun 512 målinger ved indgangen, da det kun er muligt at hente hele blokke, dvs. filteret skal hente 640 målinger og ved at bruge 512 af dem, vil der være en rest indtil en ny portion data akkumuleres. Således skal vores filter, udover dets primære arbejde, også sikre hjælpefunktioner til mellemlagring af inddata. Udviklerne af mediestrømmeren samt løsningen på denne generelle opgave har udviklet et specielt objekt — MSBufferizer (buffer), der løser denne opgave ved hjælp af tuples.

Bufferizer (MSBufferizer)

Dette objekt vil akkumulere inddata inde i filteret og begynde at returnere dem til behandling, så snart mængden af information er tilstrækkelig til at køre filteralgoritmen. Mens bufferizer akkumulerer data, vil filteret køre i tomgang, uden at bruge computerkraft fra processoren. Men så snart funktionen til at læse fra bufferizer returnerer en værdi, der er forskellig fra nul, begynder filterets process() funktion at hente og behandle data fra bufferizer i portioner af den ønskede størrelse, indtil de er opbrugt.
De endnu ikke anvendte data forbliver i bufferizer som det første element i tuplen, hvortil de efterfølgende blokke inddata knyttes.

Strukturen, der beskriver bufferizer:

struct _MSBufferizer{
queue_t q; /* Meddelelse kø. */
int size; /* Den samlede størrelse af data, der i øjeblikket findes i bufferizer. */
};
typedef struct _MSBufferizer MSBufferizer;

Functions for Working with MSBufferizer

Oprettelse af en ny instans af bufferizer:

MSBufferizer * ms_bufferizer_new(void);

Hukommelse tildeles, initialiseres i ms_bufferizer_init() og en peger returneres.

Initialiseringsfunktionen:

void ms_bufferizer_init(MSBufferizer *obj); 

Køen q, felt størrelse initialiseres til nul.

Tilføjelse af besked:

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

Beskrivelse m tilføjes til køen. Den beregnede størrelse af datablokke tilføjes til størrelse.

Overførsel til bufferizer af alle meddelelser i queuedata-linket q:

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

Flytning af meddelelser fra linket q til bufferizer udføres ved hjælp af funktionen ms_bufferizer_put().

Læsning fra bufferizer:

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

Hvis størrelsen af de akkumulerede data i bufferizer er mindre end anmodet (datalen), returnerer funktionen nul, og data kopieres ikke til data. Ellers udføres en sekventiel kopiering af data fra tuplerne i bufferen. Efter kopiering fjernes tuplet, og hukommelsen frigøres. Kopieringen afsluttes, når datalen bytes er kopieret. Hvis pladsen løber tør midt i en datablok, vil denne besked vise, at datablokken vil blive forkortet til den resterende ukopierede del. Ved næste kald vil kopieringen fortsætte fra dette sted.

Læsning af antallet af data, der er tilgængelige i øjeblikket i bufferen:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Returnerer feltet størrelse af bufferen.

Springe en del af dataene i bufferen over:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Det angivne antal bytes data hentes og springes over. De ældste data springes over.

Fjernelse af alle beskeder i bufferen:

void ms_bufferizer_flush(MSBufferizer *obj); 

Data tælleren nulstilles.

Fjernelse af alle beskeder i bufferen:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Nulstilling af tælleren udføres ikke.

Sletning af bufferen og frigivelse af hukommelse:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Eksempler på brug af bufferen kan findes i kildekoden til flere mediestrømningsfiltre. For eksempel i MS_L16_ENC-filteret, som udfører byteombytning i prøver fra netværksrækkefølge til værtens rækkefølge: l16.c

I den næste artikel vil vi se på vurderingen af belastningen på tickeren og metoder til at håndtere overdreven beregningsbelastning i mediastreameren.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster