Vi studerar VoIP-motorn Mediastreamer2. Del 11

Artikelmaterialet Àr hÀmtat frÄn min Zen-kanal.

Vi studerar VoIP-motorn Mediastreamer2. Del 11

Datatransfer mekanism

  • Data block dblk_t
  • Message mblk_t
  • Funktioner för arbete med meddelanden mblk_t
  • Kö queue_t
  • Funktioner för arbete med köer queue_t
  • Filteranslutning
  • Signalknutpunkt i databehandlingsgrafen
  • Bakom kulisserna aktivitet av tickern
  • Buffert (MSBufferizer)
  • Funktioner för arbete med MSBufferizer

Tidigare artikeln Vi har utvecklat vÄrt eget filter. Denna artikel kommer att Àgnas Ät strukturen av den interna mekanismen för datatransfer mellan filter i mediastreamern. Detta kommer sedan att möjliggöra skrivandet av sofistikerade filter med mindre anstrÀngning.

Datatransfer mekanism

Datatransfer i mediastreamern utförs med hjÀlp av köer som beskrivs av strukturen queue_t. Genom köerna flyttas kedjor av meddelanden av typen mblk_t, som i sig sjÀlva inte innehÄller signaldata, utan endast referenser till det föregÄende, nÀsta meddelande och till datablocket. Dessutom vill jag sÀrskilt pÄpeka att det finns ett fÀlt för en referens till ett meddelande av samma typ, vilket möjliggör organiseringen av en enkel lÀnkad lista av meddelanden. En grupp meddelanden, sammanlÀnkade pÄ detta sÀtt, kommer vi att kalla en tupel. SÄledes kan vilket som helst element i kön vara ett enskilt meddelande mblk_t, eller ocksÄ vara huvudet pÄ en tupel av meddelanden mblk_t. Varje meddelande i tupeln kan ha sitt eget underliggande datablock. Varför vi behöver tupeln kommer vi att diskutera lite senare.

Som nĂ€mnts ovan innehĂ„ller sjĂ€lva meddelandet inte datablocket, istĂ€llet innehĂ„ller det endast en pekare till det minnesomrĂ„de dĂ€r blocket lagras. I denna del liknar den övergripande bilden av mediastreamerns arbete en lager av dörrar i tecknad filmen 'Monsterföretaget', dĂ€r dörrarna (datareferenser — rum) rör sig med galen hastighet lĂ€ngs hĂ€ngande transportband, medan sjĂ€lva rummen förblir stilla.

Nu, nÀr vi gÄr igenom hierarkin underifrÄn och upp, lÄt oss utforska detaljerat de nÀmnda entiteterna i datatransfermekanismen i mediastreamern.

Datablock dblk_t

Datablocket bestÄr av en rubrik och en datarbuffert. Rubriken beskrivs av följande struktur,

typedef struct datab
{
unsigned char *db_base; // Pekare till början av databufferten.
unsigned char *db_lim;  // Pekare till slutet av databufferten.
void (*db_freefn)(void*); // Funktion för att frigöra minne vid borttagning av block.
int db_ref; // ReferensrÀknare.
} dblk_t;

Strukturens fĂ€lt innehĂ„ller pekare till början av bufferten, slutet av bufferten och funktionen för att ta bort databufferten. Den sista posten i rubriken db_ref – Ă€r en rĂ€knare för referenser, nĂ€r den nĂ„r noll fungerar det som en signal för att ta bort denna block frĂ„n minnet. Om datablocket har skapats av funktionen datab_alloc() , kommer databufferten att placeras i minnet direkt efter rubriken. I alla andra fall kan bufferten ligga nĂ„gon annanstans. I databufferten kommer signaldata eller andra data som vi vill bearbeta med filter att lagras.

Ett nytt instans av datablocket skapas med hjÀlp av funktionen:

dblk_t *datab_alloc(int size);

Som inparametern fĂ„r den storleken pĂ„ de data som blocket ska lagra. Minnet allokeras mer, för att i början av det allokerade minnet placera rubriken – strukturen datab. Men nĂ€r andra funktioner anvĂ€nds sĂ„ sker det inte alltid, i vissa fall kan databufferten ligga separerat frĂ„n datablocket. Strukturens fĂ€lt konfigureras vid skapandet sĂ„ att dess fĂ€lt db_base pekar pĂ„ början av datamĂ€ngden, och db_lim pĂ„ dess slut. ReferensrĂ€knaren db_ref stĂ€lls in pĂ„ ett. Pekaren till funktionen för att rensa data stĂ€lls in pĂ„ noll.

Meddelandet mblk_t

Som nÀmnts har köens element typ mblk_t, som definieras enligt följande:

typedef struct msgb
{
  struct msgb *b_prev;   // Pekare till föregÄende element i listan.
  struct msgb *b_next;   // Pekare till nÀsta element i listan.
  struct msgb *b_cont;   // Pekare för att klistra in andra meddelanden till meddelandet, för att skapa tuple meddelanden.
  struct datab *b_datap; // Pekare till datablokens struktur.
  unsigned char *b_rptr; // Pekare till början av datamÀngden för att lÀsa data frÄn bufferten b_datap.
  unsigned char *b_wptr; // Pekare till början av datamÀngden för att skriva data till bufferten b_datap.
  uint32_t reserved1;    // ReservfÀlt1, mediastreamer placerar dÀr serviceinformation. 
  uint32_t reserved2;    // ReservfÀlt2, mediastreamer placerar dÀr serviceinformation.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktur mblk_t innehÄller i början pekare b_prev, b_next, vilka Àr nödvÀndiga för att organisera en dubbellÀnkad lista (som Àr kön queue_t).

Sedan följer pekaren b_cont, som endast anvÀnds nÀr meddelandet gÄr in i tuplen. För det sista meddelandet i tuplen förblir denna pekare noll.

HÀr ser vi en pekare till datablocket b_datap, vilket Àr anledningen till att meddelandet existerar. DÀrefter följer pekare till omrÄdet inom datablocket. FÀltet b_rptr anger platsen frÄn vilken data kommer att lÀsas frÄn buffern. FÀltet b_wptr anger platsen dÀr skrivning till buffern kommer att ske.

De ÄterstÄende fÀlten Àr av servicekaraktÀr och relaterar inte till hur datatransmissionsmekanismen fungerar.

Nedan visas ett enskilt meddelande med namnet m1 och datablocket d1.
Vi studerar VoIP-motorn Mediastreamer2. Del 11
I nÀsta bild visas en tuppel av tre meddelanden m1, m1_1, m1_2.
Vi studerar VoIP-motorn Mediastreamer2. Del 11

Funktioner för att hantera meddelanden mblk_t

Ett nytt meddelande mblk_t skapas av funktionen:

mblk_t *allocb(int size, int pri); 

den allokerar ett nytt meddelande mblk_t med ett datablock av angiven storlek size, den andra argumentet — pri anvĂ€nds inte i den aktuella versionen av biblioteken. Det ska förbli noll. Under funktionens gĂ„ng kommer minne att avsĂ€ttas för strukturen av det nya meddelandet och funktionen mblk_init(), som nollstĂ€ller alla fĂ€lt i den skapade instansen av strukturen och sedan, med hjĂ€lp av den nĂ€mnda tidigare, datab_alloc(), skapa databufferten. DĂ€refter kommer fĂ€lten i strukturen att stĂ€llas in:

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

Vid utgÄngen fÄr vi ett nytt meddelande med initierade fÀlt och en tom databuffert. För att lÀgga till data i meddelandet mÄste man kopiera dem till datablocket:

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

w data — pekaren till datakĂ€llan, och size — dess storlek.
dÀrefter mÄste man uppdatera pekaren till skrivpunkten sÄ att den Äter pekar pÄ början av det lediga omrÄdet i bufferten:

msg->b_wptr = msg->b_wptr + size

Om det krÀvs att skapa ett meddelande frÄn en redan existerande buffer, utan att kopiera, anvÀnds funktionen:

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

Funktionen kommer efter skapandet av meddelandet och datablocket, att stÀlla in dess pekare till data vid adressen buf. D.v.s. i detta fall ligger databufferten inte efter fÀlten i datablocket, som det var vid skapandet av datablocket med funktionen datab_alloc()Den överförda funktionen buffert med data kommer att förbli dÀr den var, men kommer att styra den nyss skapade blockhuvudet via pekare, som i sin tur gÄr till meddelandet.

Ett meddelande mblk_t kan ha flera datablok i följd kopplade till sig. Detta görs av funktionen:

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

mp — meddelandet som kommer att fĂ„ ytterligare ett datablok;
data — en pekare pĂ„ det blok som en kopia kommer att lĂ€ggas till i meddelandet;
size — storlek pĂ„ data;
pad — flagga som anger att den tilldelade minnesstorleken ska justeras till en 4-byte-grĂ€ns (fyllning kommer att göras med nollor).

Om det finns tillrÀckligt med utrymme i den befintliga meddelandebufferten, kommer de nya uppgifterna att kopplas pÄ efter de redan befintliga dÀr. Om det fria utrymmet i meddelandebufferten blir mindre Àn size, skapas ett nytt meddelande med tillrÀckligt stor buffert och data kopieras till dess buffert. Detta nya meddelande kopplas till det ursprungliga via pekaren b_cont. I det hÀr fallet omvandlas meddelandet till en tuple.

Om du behöver lÀgga till ytterligare ett datablok i tuplen, mÄste du anvÀnda funktionen:

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

den kommer att hitta det senaste meddelandet i tuplen (det kommer att vara noll) och anropa funktionen b_cont appendb() För att ta reda pÄ storleken pÄ uppgifterna i meddelandet eller tuplen kan du anvÀnda funktionen:.

int msgdsize(const mblk_t *mp);

den kommer att gÄ igenom alla meddelanden i tuplen och returnera det totala antalet data i data-bufferarna för dessa meddelanden. För varje meddelande berÀknas datamÀngden sÄ hÀr:

mp->b_wptr - mp->b_rptr

 För att förena tvÄ tupler anvÀnds funktionen:

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

den kopplar tuplen

newm till slutet av tuplen och returnerar en pekare pÄ det senaste meddelandet i den resulterande tuplen. mp Vid behov kan tuplen omvandlas till ett enda meddelande med ett enda datablok, detta görs av funktionen:

void msgpullup(mblk_t *mp,int len);

om argumentet

Ă€r lika med -1, sĂ„ bestĂ€ms storleken pĂ„ den tilldelade bufferten automatiskt. Om len Ă€r lika med -1, sĂ„ bestĂ€ms storleken pĂ„ den avledda bufferten automatiskt. Om len Om det Ă€r ett positivt tal kommer en buffert av denna storlek att skapas och data frĂ„n tuplemeddelandena kommer att kopieras dit. Om bufferten tar slut kommer kopieringen att stoppas. Det första meddelandet i tuplen fĂ„r en buffert av ny storlek med kopierade data. Övriga meddelanden kommer att raderas och minnet Ă„terförs till högen.

Vid borttagning av strukturen mblk_t beaktas referensrÀknaren för datablocket, om vid anropet freeb() den visar sig vara noll, raderas datan frÄn bufferten tillsammans med instansen mblk_t, som pekar pÄ den.

Initialisering av fÀlten i det nya meddelandet:

void mblk_init(mblk_t *mp);

LÀgg till en ny datamÀngd till meddelandet:

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

Om de nya data inte fÄr plats i det tillgÀngliga utrymmet i meddelandets datBuffert, kopplas ett separat skapat meddelande med den nödvÀndiga storleken till meddelandet (pekaren till det tillagda meddelandet stÀlls in i det första meddelandet) meddelandet blir en tuple.

LÀgg till en datamÀngd till tuplen:

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

Funktionen anropar appendb() i en loop.

Sammanfoga tvÄ tupler till en:

den kopplar tuplen

Meddelandet till slutet av tuplen kommer att anslutas till mp.

Skapa en kopia av ett enda meddelande:

mblk_t *copyb(const mblk_t *mp);

FullstÀndig kopiering av tuplen med alla datablokkar:

mblk_t *copymsg(const mblk_t *mp);

Elementen i tuplen kopieras med funktionen copyb().

Skapa en lÀtt kopia av meddelandet mblk_t. Vid detta kopieras inte datablocket, utan dess referensrÀknare ökas db_ref:

mblk_t *dupb(mblk_t *mp);

Skapa en lÀtt kopia av tuplen. Datablokkar kopieras inte, utan endast deras referensrÀknare ökas db_ref:

mblk_t *dupmsg(mblk_t* m);

Sammanfoga alla meddelanden i tuplen till ett meddelande:

void msgpullup(mblk_t *mp, size_t len);

Om argumentet len Àr lika med -1, bestÀms storleken pÄ bufferten automatiskt.

Ta bort meddelandet, tuplen:

void freemsg(mblk_t *mp);

ReferensrÀknaren för datablocket minskas med ett. Om den nÄr noll raderas Àven datablocket.

BerÀkning av den totala datavolymen i meddelandet eller tuplen.

size_t msgdsize(const mblk_t *mp);

HÀmta meddelandet frÄn köns svans:

mblk_t *ms_queue_peek_last(q);

Kopiera innehÄllet i de reserverade fÀlten frÄn ett meddelande till ett annat (det finns i dessa fÀlt flaggor som anvÀnds av medieströmmen):

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

Kö queue_t

Kö-systemet i medieströmmen Àr implementerat som en ringdoubellista. Varje element i listan innehÄller en pekare till en datablob med signalavlÀsningar. Det innebÀr att det bara Àr pekarna till databloben som flyttas i ordning, medan sjÀlva datan förblir stilla. Det vill sÀga att det endast Àr lÀnkarna till dem som flyttas.
Strukturen som beskriver kön queue_t, visas nedan:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Tom" element i kön, pekar inte pÄ data, anvÀnds endast för att styra kön. Vid initiering av kön (qinit()) stÀlls dess pekare in sÄ att de pekar pÄ sig sjÀlva. */
   int q_mcount;        // Antal element i kön.
} queue_t;

Strukturen innehĂ„ller ett fĂ€lt — en pekare _q_stopper av typen *mblk_t, den pekar pĂ„ det första elementet (meddelandet) i kön. Det andra fĂ€ltet i strukturen Ă€r en rĂ€knare för meddelanden som finns i kön.
I bilden nedan visas kön med namnet q1, som innehÄller 4 meddelanden m1,m2, m3, m4.
Vi studerar VoIP-motorn Mediastreamer2. Del 11
I nÀsta bild visas kön med namnet q1, som innehÄller 4 meddelanden m1,m2, m3, m4. Meddelandet m2 Àr huvudknuten, som har ytterligare tvÄ meddelanden m2_1 och m2_2.

Vi studerar VoIP-motorn Mediastreamer2. Del 11

Funktioner för arbete med köer queue_t

Initiering av kön:

void qinit(queue_t *q);

FÀltet _q_stopper (hÀdanefter kommer vi att kalla det "stoppare") initieras av funktionen mblk_init(), dess pekare för det föregÄende elementet och nÀsta element stÀlls in sÄ att de pekar pÄ sig sjÀlva. RÀknaren för element i kön nollstÀlls.

TillÀgg av ett nytt element (meddelande):

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

Det nya elementet m lÀggs till i slutet av listan, pekarna för elementet stÀlls in sÄ att stoppare blir dess nÀsta element och det blir det föregÄende elementet för stoppare. RÀknaren för element i kön inkrementeras.

Uttag av ett element frÄn kön:

mblk_t * getq(queue_t *q); 

det meddelande som stÄr efter stoppare tas bort, rÀknaren för element minskas. Om det inte finns nÄgra element i kön förutom stoppare, returneras 0.

InsÀttning av meddelande i kön:

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

Element mp infogas före elementet emp. Om emp=0, sÄ lÀggs meddelandet till i slutet av kön.

Uttag av meddelande frÄn huvudet av kön:

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

RÀknaren för element minskas.

LÀsa pekaren till det första elementet i kön:

mblk_t * peekq(queue_t *q); 

Borttagning av alla element frÄn kön med radering av sjÀlva elementen:

void flushq(queue_t *q, int how);

Argumentet how anvÀnds inte. Antalet köelement sÀtts till noll.

Makro för att lÀsa pekaren pÄ det sista köelementet:

mblk_t * qlast(queue_t *q);

NÀr man arbetar med meddelandeköer bör man komma ihÄg att vid anrop av ms_queue_put(q, m) med en nullpekare till meddelandet, slutar funktionen i en oÀndlig loop. Ditt program kommer att lÄsa sig. Detsamma gÀller ms_queue_next(q, m).

Filteranslutning

Den beskrivna kön anvÀnds för att överföra meddelanden frÄn en filter till en annan eller frÄn en till flera filter. Filter och deras anslutningar bildar en riktad graf. IngÄng eller utgÄng av filtret kallas med det generella ordet "pin". För att beskriva ordningen för anslutningar mellan filter, anvÀnds begreppet "signalpunkt" i medieströmmaren. En signalpunkt Àr en struktur _MSCPoint, som innehÄller en pekare pÄ filtret och numret pÄ en av dess pinnar, vilket beskriver anslutningen av en av ingÄngarna eller utgÄngarna av filtret.

Signalknutpunkt i databehandlingsgrafen

typedef struct _MSCPoint{
struct _MSFilter *filter; // Pekare pÄ medieströmmarfiltret.
int pin;                        // Numret pÄ en av ingÄngarna eller utgÄngarna av filtret, dvs. pin.
} MSCPoint;

Pinnarna pÄ filtren numreras frÄn noll.

Anslutningen av tvÄ pinnar via en meddelandekö beskrivs av strukturen _MSQueue, som innehÄller meddelandekön och pekare pÄ tvÄ signalpunkter som den ansluter:

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

Vi kallar denna struktur för en signal-lÀnk. Varje medieströmmarfiltret innehÄller en tabell med ingÄngslÀnkar och en tabell med utgÄngslÀnkar (MSQueue). Storleken pÄ tabellerna anges vid skapandet av filtret, detta har vi redan gjort med hjÀlp av den exporterade variabeln av typen MSFilterDesc, nÀr vi utvecklade vÄr egen filter. Nedan visas strukturen som beskriver vilket filter som helst i medieströmmaren, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Pekare till filterbeskrivningen. */
    /* Skyddade attribut, de kan inte flyttas eller tas bort, annars fungerar plugins fel. */
    ms_mutex_t lock;      /* Semafor. */
    MSQueue **inputs;     /* Tabell med ingÄngslÀnkar. */
    MSQueue **outputs;    /* Tabell med utgÄngslÀnkar. */
    struct _MSFactory *factory; /* Pekare till fabriken som skapade detta instans av filtret. */
    void *padding;              /* AnvÀnds inte, kommer att anvÀndas om skyddade fÀlt lÀggs till. */
    void *data;                 /* Pekare till en godtycklig struktur för att lagra interna filterdata och mellanresultat. */
    struct _MSTicker *ticker;   /* Pekare till tickern, som inte fÄr vara null nÀr funktionen process() anropas. */
    /* privata attribut, de kan flyttas och Àndras nÀr som helst */
    MSList *notify_callbacks; /* Lista med Äterkopplingsfunktioner som anvÀnds för att hantera filterhÀndelser. */
    uint32_t last_tick;       /* Nummer pÄ den senaste takten nÀr process() anropades. */
    MSFilterStats *stats;     /* Filterstatistik. */
    int postponed_task; /* Antal uppskjutna uppgifter. Vissa filter kan skjuta upp databehandling (anrop av process()) över flera takter. */
    bool_t seen;  /* Flagg som anvÀnds av tickern för att markera att denna instans av filtret redan har hanterats pÄ denna takt. */
};
typedef struct _MSFilter MSFilter;

NÀr vi i C-programmet har kopplat ihop filtren enligt vÄr plan (men inte kopplat tickern), har vi dÀrmed skapat en riktad graf, vars noder Àr instanser av strukturen MSFilter, och kanterna Àr instanser av lÀnkar MSQueue.

Bakom kulisserna aktivitet av tickern

NÀr jag sa till dig att tickern Àr en taktkÀlla för filtren, var det inte hela sanningen om den. Tickern Àr ett objekt som kör funktionerna process() för alla filter i schemat (grafen) som den Àr kopplad till. NÀr vi kopplar tickern till ett filter i grafen, visar vi tickern grafen som den frÄn och med nu kommer att hantera tills vi kopplar bort den. Efter att ha kopplat, börjar tickern inspektera den tilldelade grafen och skapar en lista över de filter som ingÄr i den. För att inte "rÀkna" ett filter tvÄ gÄnger, markerar den upptÀckta filter genom att sÀtta en flagga seen. Söket genomförs i lÀnktabeller som varje filter har.

Under sin introduktionstur kontrollerar tickern om det finns Ätminstone ett filter bland filtren som fungerar som en kÀlla för datablokker. Om inga hittas, betraktas grafen som felaktig och tickern avslutar sin funktion.

Om grafen visade sig vara "korrekt", anropas en funktion för varje hittat filter för att initiera det. preprocess(). SÄ snart det Àr dags för nÀsta bearbetningscykel (som standard var tionde millisekund), anropar tickern en funktion. process() för alla tidigare funna kÀllfilter och sedan för de övriga filtren i listan. Om filtret har ingÄngslÀnkar, upprepas anropet av funktionen process() tills dess att köerna för ingÄngslÀnkarna Àr tomma. DÀrefter gÄr den vidare till nÀsta filter i listan och "rullar" detta tills ingÄngslÀnkarna Àr fria frÄn meddelanden. Tickern gÄr frÄn filter till filter tills listan Àr slut. Bearbetningen av cykeln avslutas hÀr.

Nu gÄr vi tillbaka till tuplarna och pratar om varför en sÄdan entitet lades till i medieströmmen. I allmÀnhet stÀmmer inte datavolymen som algoritmen som arbetar inuti filtret behöver överens med storleken pÄ buffrarna för de inkommande datablokkerna. LÄt oss sÀga att vi skriver ett filter som gör snabb Fourier-transformering, vilket per definition bara kan bearbeta datablokker vars storlek Àr en exponent av tvÄ. LÄt oss sÀga 512 prover. Om data genereras frÄn en telefonlinje, kommer buffern för varje inkommande meddelande att ge oss 160 prover av signalen. Det finns en frestelse att inte hÀmta data frÄn ingÄngen förrÀn det finns ett tillrÀckligt antal data. Men i det hÀr fallet uppstÄr det en konflikt med tickern, som utan framgÄng kommer att försöka rulla filtret tills ingÄngslÀnken Àr tom. Tidigare betecknade vi denna regel som den tredje principen för hur filtret fungerar. I enlighet med denna princip mÄste funktionen process() för filtret hÀmta alla data frÄn ingÄngsköerna.

Dessutom kan man inte hĂ€mta bara 512 vĂ€rden frĂ„n ingĂ„ngen, eftersom de mĂ„ste hĂ€mtas i hela block, det vill sĂ€ga filtret mĂ„ste hĂ€mta 640 vĂ€rden och anvĂ€nda 512 av dem, medan resten sparas tills en ny portion data ackumuleras. DĂ€rför, förutom sin huvudfunktion, mĂ„ste vĂ„rt filter ocksĂ„ tillhandahĂ„lla stödjande Ă„tgĂ€rder för mellanlagring av ingĂ„ngsdata. Utvecklarna av medieströmmen och lösningen pĂ„ denna gemensamma uppgift har utvecklat ett speciellt objekt – MSBufferizer (buffer), som löser denna uppgift med hjĂ€lp av tuples.

Buffert (MSBufferizer)

Detta objekt kommer att ackumulera ingÄngsdata inuti filtret och börja skicka dem för bearbetning sÄ snart mÀngden information Àr tillrÀcklig för att köra filtreringsalgoritmen. Medan bufferten samlar data kommer filtret att arbeta i tomgÄngslÀge och inte anvÀnda nÄgon processorkraft. Men sÄ snart funktionen för att lÀsa frÄn bufferten returnerar ett vÀrde som Àr olika frÄn noll, börjar funktionen process() filtrera och bearbeta data frÄn bufferten i portioner av rÀtt storlek, tills de Àr uttömda.
De hittills oanvÀnda data förblir i bufferten som det första elementet i tuple, till vilket hÀrföljer följande block av ingÄngsdata.

Strukturen som beskriver bufferten:

struct _MSBufferizer{
queue_t q; /* Meddelandekö. */
int size; /* Total storlek pÄ data i bufferten för tillfÀllet. */
};
typedef struct _MSBufferizer MSBufferizer;

Funktioner för arbete med MSBufferizer

Skapande av en ny instans av bufferten:

MSBufferizer * ms_bufferizer_new(void);

Minnet allokeras, initieras i ms_bufferizer_init() och en pekare returneras.

Initieringsfunktion:

void ms_bufferizer_init(MSBufferizer *obj); 

Kön q, fÀlt size initieras till noll.

LĂ€gg till meddelande:

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

Meddelandet m lÀggs till kön. Den berÀknade storleken av datablokkerna lÀggs till size.

Överföring till bufferten av alla meddelanden frĂ„n datakön q:

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

Överföring av meddelanden frĂ„n kön q till bufferten görs med hjĂ€lp av funktionen ms_bufferizer_put().

LÀsa frÄn bufferten:

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

Om storleken pÄ de ackumulerade i bufferten data Àr mindre Àn den begÀrda storleken (datalen), sÄ returnerar funktionen noll, och datakopiering till data utförs inte. Annars utförs sekventiell kopiering av data frÄn tupplarna som finns i buffertminnet. Efter kopiering tas tupplarna bort och minnet frigörs. Kopieringen avslutas nÀr datalen byte har kopierats. Om utrymmet tar slut mitt i en datablokk, kommer i detta meddelande datablokken att förkortas till den ÄterstÄende icke-kopierade delen. Vid nÀsta anrop fortsÀtter kopieringen frÄn denna punkt.

LÀsa antalet data som Àr tillgÀngliga för nÀrvarande i buffertminnet:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Returnerar fÀltet size buffertminnet.

Kasta bort en del av data som finns i buffertminnet:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Det angivna antalet bytes data extraheras och kastas bort. De Àldsta data kastas bort.

Ta bort alla meddelanden i buffertminnet:

void ms_bufferizer_flush(MSBufferizer *obj); 

Datacounten ÄterstÀlls till noll.

Ta bort alla meddelanden i buffertminnet:

void ms_bufferizer_uninit(MSBufferizer *obj); 

ÅterstĂ€llning av datarĂ€knaren utförs inte.

Ta bort buffertminnet och frigöra minnet:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Exempel pÄ anvÀndning av buffertminnet kan hittas i kÀllkoden för flera mediastreamerfilter. Till exempel i filtret MS_L16_ENC, som utför byteordning i nÀtverksordning, i vÀrdordning: l16.c

I nÀsta artikel kommer vi att titta pÄ hur man bedömer belastningen pÄ tickern och hur man hanterar överdriven berÀkningsbelastning i mediastreamern.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster