A Mediastreamer2 VoIP motor tanulmányozása. 11. rész

A cikk anyaga az én Dzen csatornámból származik.

A Mediastreamer2 VoIP motor tanulmányozása. 11. rész

Adatok áthelyezési mechanizmusa

  • dblk_t adatblokk
  • mblk_t üzenet
  • mblk_t üzenetekkel kapcsolatos funkciók
  • queue_t sor
  • queue_t sorokkal kapcsolatos funkciók
  • Szűrők kapcsolata
  • Jelzési pont az adatfeldolgozó gráfban
  • A ticker mögötti tevékenység
  • Pufferelő (MSBufferizer)
  • MSBufferizer-rel kapcsolatos funkciók

A múltban a Spotify döntéshozatali módszertanáról. Egyre több felhasználó preferálja a zenehallgatást mobilon a streaming szolgáltatásban, de a cégnél túlnyomórészt webprogramozók dolgoznak. A Spotify elemezte ezt a helyzetet, és úgy döntött, hogy több mobilfejlesztőt vesz fel, valamint átnevezi a webes szakembereket új irányba: Kifejlesztettük a saját szűrőnket. Ehhez a cikkhez az adatok szűrők közötti belső áthelyezési mechanizmusára fókuszálunk. Ez lehetővé teszi, hogy a jövőben kifinomultabb szűrőket írjunk kevesebb erőfeszítéssel.

Adatok áthelyezési mechanizmusa

Az adatok áthelyezése a mediastreamerben a következő struktúrával leírt sorok segítségével történik queue_t. A sorokban üzenetláncok mozognak, amelyek típusa mblk_t, amelyek önállóan nem tartalmazzák a jeladatszignált, hanem csak hivatkozásokat a megelőző, következő üzenetre és az adatblokkokra. Továbbá különös hangsúlyt szeretnék fektetni arra, hogy van egy mező, amely hivatkozhat egy azonos típusú üzenetre, amely lehetővé teszi az üzenetekből álló egyirányú lista létrehozását. Az ilyen listával összekapcsolt üzenetek csoportját halmaznak nevezzük. Így bármely sorban lévő elem lehet egyetlen üzenet mblk_t, vagy lehet a halmaz első üzenete mblk_t. Minden halmazbeli üzenetnek lehet saját adatblokkjai. Azért van szükség a halmazokra, ezt később meg fogjuk vitatni.

Ahogy azt már említettük, az üzenet önmagában nem tartalmaz adatblokkot, ehelyett csak egy pointert tartalmaz arra a memóriaterületre, ahol az adatblokk tárolva van. E részben a mediastreamer működésének összképe hasonlít a "Szörny Rt." című mese ajtóinak raktárához, ahol az ajtók (adatok hivatkozásai - szobák) őrült sebességgel mozognak a függőleges szállítószalagokon, míg a szobák maguk mozdulatlanok maradnak.

Most, lépésről lépésre magyarázzuk el az adatok átadásának mechanizmusának részleteit a mediastreamerben.

Adatblokk dblk_t

Az adatblokk egy fejlécből és egy adatpufferből áll. A fejlécet az alábbi struktúra írja le,

typedef struct datab
{
unsigned char *db_base; // Pointer az adatpuffer kezdetére.
unsigned char *db_lim;  // Pointer az adatpuffer végére.
void (*db_freefn)(void*); // Memóriafelszabadító függvény a blokk törlésénél.
int db_ref; // Hivatkozásszámláló.
} dblk_t;

A struktúrák mezői tartalmazzák a puffer kezdetének és végének mutatóit, valamint az adatpuffer eltávolításának funkcióját. Az utolsó elem a fejlécben db_ref — hivatkozási számláló, ha nullára csökken, ez a jelzés az érintett blokk memóriából való eltávolítására. Ha az adatblokkot a datab_alloc() függvény hozta létre, akkor az adatpuffer közvetlenül a fejléc után helyezkedik el a memóriában. Minden más esetben az adatpuffer külön helyen lehet. Az adatpufferben a jelzési számlálók vagy más adatok találhatók, amelyeket szűrőkön keresztül szeretnénk feldolgozni.

Egy új adatblokk példányát a következő függvénnyel hozzuk létre:

dblk_t *datab_alloc(int size);

A bemeneti paraméterként az adat méretét adjuk meg, amelyet a blokk tárolni fog. A memória egy része többet foglal magába, hogy a lefoglalt memória elején elhelyezhessük a fejlécet - a databstruktúrát. De más függvények használatakor ez nem mindig így van, bizonyos esetekben az adatpuffer külön helyen helyezkedik el az adatblokk fejlécétől. A világban a struktúrák mezőit úgy állítják be, hogy a db_base a adatok területének kezdetére mutasson, míg db_lim a vége felé mutasson. A hivatkozási számláló db_ref 1-re van beállítva. Az adat törlési funkció mutatóját nullára állítják.

Üzenet mblk_t

Ahogy mondtuk, a sor elemei a következő típusúak: mblk_t, és a következőképpen van meghatározva:

typedef struct msgb
{
  struct msgb *b_prev;   // A lista előző elemére mutató mutató.
  struct msgb *b_next;   // A lista következő elemére mutató mutató.
  struct msgb *b_cont;   // Egy másik üzenethez való kapcsolódásra szolgáló mutató, üzenetcsomagok létrehozásához.
  struct datab *b_datap; // Az adatblokk struktúrájára mutató mutató.
  unsigned char *b_rptr; // Az b_datap pufferrel kapcsolatos adatok olvasására szolgáló terület kezdetére mutató mutató.
  unsigned char *b_wptr; // Az b_datap pufferrel kapcsolatos adatok írására szolgáló terület kezdetére mutató mutató.
  uint32_t reserved1;    // 1. fenntartott mező, amelybe a médiastreamer segédinformációt helyez el.
  uint32_t reserved2;    // 2. fenntartott mező, amelybe a médiastreamer segédinformációt helyez el.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktúra mblk_t a kezdeti mutatók tartalmazzák b_prev, b_next, amelyek a kétszeres láncolt lista (amely a sor) kialakításához szükségesek, queue_t).

Ezután a mutató következik b_cont, amely csak akkor használatos, amikor az üzenet belekerül a tuple-be. Az utolsó üzenetben a tuple-ben ez a mutató nullát ad meg.

Ezután látjuk az adatrész blokkra mutató mutatót b_datap, amely miatt az üzenet létezik. Ezt követően mutatók következnek, amelyek a blokk adataira mutatnak a bufferben. A mező b_rptr megmutatja azt a helyet, ahonnan az adatokat olvasni fogják a bufferről. A mező b_wptr megmutatja azt a helyet, ahonnan az adatok írásra kerülnek a bufferbe.

A fennmaradó mezők szolgálati jellegűek, és nem kapcsolódnak az adatátviteli mechanizmus működéséhez.

Az alábbiakban látható egyetlen üzenet neve m1 és az adatrész d1.
A Mediastreamer2 VoIP motor tanulmányozása. 11. rész
A következő ábrán három üzenet tuple-je látható m1, m1_1, m1_2.
A Mediastreamer2 VoIP motor tanulmányozása. 11. rész

Az üzenetekkel kapcsolatos funkciók mblk_t

Új üzenet mblk_t a következő funkció segítségével jön létre:

mblk_t *allocb(int size, int pri); 

ez új üzenetet helyez el a memóriában mblk_t a megadott méretű adatblokk méret, a második argumentum — pri nem használatos a vizsgált verzióban. Ennek nullának kell maradnia. A függvény futása során memória lesz lefoglalva az új üzenet struktúrájának, és meghívásra kerül a mblk_init(), amely nullázza az összes mezőt a létrehozott struktúra példányában, majd az előbb említett módon datab_alloc(), létrehozza az adatbufert. Ezt követően a mezők beállítása fog megtörténni a struktúrában:

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

A kimeneten új üzenetet kapunk inicializált mezőkkel és üres adatbuferral. Az üzenethez adatok hozzáadásához másolni kell őket az adatblokk bufferébe:

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

ahol data — a forrásra mutató mutató, és méret — az adatok mérete.
ezután frissíteni kell a írási mutatót, hogy ismét a buffer szabad területeinek kezdetére mutasson:

msg->b_wptr = msg->b_wptr + size

Ha üzenetet kell létrehozni egy már meglévő bufferből másolás nélkül, akkor a következő funkció használható:

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

A funkció az üzenet és az adatblokk struktúra létrehozása után beállítja a mutatókat az adatokra a címhez buf. Tehát, ebben az esetben az adatbuffer nem helyezkedik el a hólyag adatainak címkecímét követően, ahogy az adatblokknak a funkció által történő létrehozásakor volt. datab_alloc()A funkciónak átadott adatpuffer ott marad, ahol volt, de mutatók segítségével az újonnan létrehozott adatblokk fejlécére utal, amely a kapcsolódó üzenetre mutat.

Egy üzenethez mblk_t több adatblokk is hozzáfűzhető. Ezt a következő függvény végzi:

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

mp — az üzenet, amelyhez egy új adatblokkot fognak hozzáadni;
data — egy blokk mutatója, amelynek a másolata kerül be az üzenetbe;
méret — az adatok mérete;
pad — a memória méretének 4 bájtos határra való igazításának jelzője (a kiegészítést nullákkal végzi).

Ha az üzenet meglévő adatpufferében elegendő hely van, akkor az új adatok a már ott lévő adatok végéhez fűződnek. Ha az üzenet adatpufferében kevesebb mint méretszabad hely áll rendelkezésre, akkor új üzenetet hoz létre, elegendő puffer mérettel, és az adatokat ennek a pufferébe másolja. Ez az új üzenet az eredetire mutat a mutatóval b_cont. Ebben az esetben az üzenet tuple-é alakul.

Ha a tuple-hez újabb adatblokkot kell hozzáadni, akkor a következő függvényt kell használni:

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

Ez megkeresi a tuple utolsó üzenetét (amelynek b_cont nulla lesz) és meghívja a függvényt az üzenethez appendb().

Azt az adatméretet, amely az üzenetben vagy a tuple-ben van, a következő függvénnyel lehet lekérdezni:

int msgdsize(const mblk_t *mp);

Ez végigfut az összes üzeneten a tuple-ért és visszaadja az ezekben a pufferekben lévő adatok összmennyiségét. Minden üzenet esetében az adatok mennyisége így számolható:

 mp->b_wptr - mp->b_rptr

Két tuple egyesítésére a következő függvényt használjuk:

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

Ez a tuple-hez fűzi newm a tuple végéhez, mp és visszaadja a kapott tuple utolsó üzenetének mutatóját.

Szükség esetén a tuple-t egyetlen üzenetté is lehet alakítani egyetlen adatblokkra, ezt a következő függvény végzi:

void msgpullup(mblk_t *mp,int len);

ha az argumentum len −1-re van állítva, akkor a lefoglalt puffer mérete automatikusan kerül meghatározásra. Ha len Ha pozitív számot adunk meg, akkor létrehozásra kerül egy ilyen méretű puffermemória, és ebbe másolják a tuple üzeneteinek adatait. Ha a puffermemória megtelik, akkor a másolás ezen a ponton leáll. Az első tuple üzenete egy új méretű puffert kap az átmásolt adatokkal. A további üzenetek törlődnek, és a memória visszakerül a heap-re.

Struktúra törlésekor mblk_t figyelembe kell venni az adatblokk hivatkozási számát, ha a híváskor freeb() az nullára csökken, akkor az adatpuffer törlődik azzal az eseménnyel együtt mblk_t, amire hivatkozik.

Új üzenet mezőinek inicializálása:

void mblk_init(mblk_t *mp);

További adatcsomag hozzáfűzése az üzenethez:

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

Ha az új adatok nem férnek el a hír puffermemóriájának szabad helyén, akkor egy külön létrehozott üzenet csatlakozik az üzenethez a megfelelő méretű pufferral (az első üzenetben beállítódik a hivatkozó a hozzáadott üzenetre) az üzenetet tuple-levé alakítja.

Adatrész hozzáadása a tuple-hoz:

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

A függvény ciklikusan hívja az appendb()-t.

Két tuple egyesítése egybe:

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

Üzenet newm az lesz csatolva mp.

Egyetlen üzenet másolatának létrehozása:

mblk_t *copyb(const mblk_t *mp);

A tuple teljes másolata az összes adatblokkal:

mblk_t *copymsg(const mblk_t *mp);

A tuple elemei a következő függvény segítségével másolandók copyb().

Egy könnyű másolat létrehozására az üzenetről mblk_t. Ekkor az adatblokk nem másolódik, hanem a hivatkozási számát növelik db_ref:

mblk_t *dupb(mblk_t *mp);

Egy könnyű másolat létrehozása a tuple-ról. Az adatblokkok nem másolódnak, csak a hivatkozási számaik növekednek. db_ref:

mblk_t *dupmsg(mblk_t* m);

Az összes üzenet összefűzése egyetlen üzenetté:

void msgpullup(mblk_t *mp, size_t len);

Ha az argumentum len egyenlő -1-el, akkor a fenntartott puffer mérete automatikusan kerül meghatározásra.

Üzenet törlése, a tuple törlése:

void freemsg(mblk_t *mp);

Az adatblokk hivatkozási száma csökken egy egységgel. Ha ezzel együtt nullára csökken, akkor az adatblokk is törlődik.

Az üzenet vagy tuple teljes adatmennyiségének megszámlálása.

size_t msgdsize(const mblk_t *mp);

Üzenet kinyerése a sor végéről:

mblk_t *ms_queue_peek_last (q);

Egy üzenet fenntartott mezőinek tartalmának átmásolása egy másik üzenetbe (valójában ezekben a mezőkben a médiastreamer által használt zászlók találhatók):

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

Sor queue_t

A médiastreamer üzenetsor egy gyűrűs kétdimenziós listaként valósul meg. Minden listaelem egy mutatót tartalmaz a jel adatait tartalmazó blokkra. Tehát csak a blokkokra mutató mutatók mozognak a sorban, míg az adatok maguk mozdulatlanok maradnak. Vagyis csak a rájuk mutató hivatkozások mozdulnak.
A sor struktúrája queue_taz alábbiak szerint látható:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Üres" elem a sorban, nem mutat adatokra, csak a sor kezelésére szolgál. A sor inicializálásakor (qinit()) a mutatói így lesznek beállítva, hogy önmagára mutassanak. */
   int q_mcount;        // Az elemek száma a sorban.
} queue_t;

A struktúra tartalmaz egy mezőt — mutatót _q_stopper típusú *mblk_t, amely az első elemre (üzenetre) mutat a sorban. A struktúra második mezője a sorban lévő üzenetek számlálója.
Az alábbi ábrán a q1 nevű sor látható, amely 4 üzenetet tartalmaz: m1, m2, m3, m4.
A Mediastreamer2 VoIP motor tanulmányozása. 11. rész
A következő ábrán a q1 nevű sor látható, amely 4 üzenetet tartalmaz: m1, m2, m3, m4. Az m2 üzenet a pár headje, amelyhez még két üzenet, m2_1 és m2_2 csatlakozik.

A Mediastreamer2 VoIP motor tanulmányozása. 11. rész

queue_t sorokkal kapcsolatos funkciók

A sor inicializálása:

void qinit(queue_t *q);

Mező _q_stopper (ezt a jövőben "stopper"-nek nevezzük) a következő függvénnyel inicializálódik, mblk_init()a mutatója az előző és a következő elemnek a saját magára való mutatásra van beállítva. Az elemek számlálója 0-ra van állítva.

Új elem (üzenet) hozzáadása:

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

Az új elem m a lista végére van hozzáadva, a mutatók beállításra kerülnek, hogy a stopper legyen a következő elem, míg ő legyen a stopper előző eleme. Az elemek számlálója növekszik.

Elem eltávolítása a sorból:

mblk_t * getq(queue_t *q); 

azt az üzenetet vonja ki, amely a stoppert követi, az elemek számlálója csökken. Ha a stoppert leszámítva a sorban nincsenek elemek, akkor 0-t ad vissza.

Üzenet beszúrása a sorba:

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

Elem mp beszúrásra kerül az emp. Ha emp=0, akkor a üzenet a sor végére kerül.

Üzenet eltávolítása a sor elejéről:

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

Az elemek számlálója csökken.

Az első elem mutatójának kiolvasása a sorban:

mblk_t * peekq(queue_t *q); 

Minden elem eltávolítása a sorból, maguknak az elemeknek az eltávolításával:

void flushq(queue_t *q, int how);

Az argumentum how nem használható. A sorba állítás elemeinek számlálója nullára van állítva.

Makró a sor utolsó elemének olvasási címére:

mblk_t * qlast(queue_t *q);

Üzenetsorokkal való munka során figyelembe kell venni, hogy a következő híváskor ms_queue_put(q, m) nulla üzenetre mutató mutatóval a függvény végtelen ciklusba kerül. A programja lefagy. Hasonlóan viselkedik ms_queue_next(q, m).

Szűrők kapcsolata

A fenti leírt sorokat üzenetek továbbítására használják az egyik szűrőtől a másikhoz vagy egy szűrőtől több szűrőhöz. A szűrők és azok összeköttetései irányított gráfot alkotnak. Egy szűrő bemenetét vagy kimenetét az általános kifejezés „tű” jelöli. A szűrők közötti összeköttetések sorrendjének leírásához a médiastreamában a „jelzőpont” fogalmát használják. A jelzőpont egy struktúra _MSCPoint, amely tartalmazza a szűrőre mutató mutatót és a tű számát, így leírja a szűrő egyik bemenetének vagy kimenetének összeköttetését.

Jelzési pont az adatfeldolgozó gráfban

typedef struct _MSCPoint{
struct _MSFilter *filter; // A médiastream szűrőre mutató mutató.
int pin; // Egy bemenet vagy kimenet számát jelenti, azaz a tűt.
} MSCPoint;

A szűrők tűit nulláról kezdődően számozzák.

Két tű összeköttetését üzenetsoron keresztül a következő struktúra írja le _MSQueue, amely tartalmazza az üzenetsort és a két jelzőpontra mutató mutatókat, amelyeket összekapcsol:

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

Ezt a struktúrát jelző linknek nevezzük. Minden médiastream szűrő tartalmaz egy belső és egy külső linkek táblázatát (MSQueue). A táblázatok mérete a szűrő létrehozásakor van megadva, amit már megtettünk az exportált "MSFilterDesc" típusú változó segítségével, amikor a saját szűrőnket fejlesztettük. Az alábbiakban bemutatott struktúra leírja bármely szűrőt a médiastreamben, MSFilterDesc, amelyeket nálunk saját szűrő fejlesztése során figyelembe vettek. A következő szerkezet bemutatja bármely szűrő leírását a médiastreamben, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* Mutató a szűrő leírására. */
    /* Védett attribútumok, amelyeket nem lehet elmozdítani vagy eltávolítani, különben megszakad a pluginek működése. */
    ms_mutex_t lock;      /* Szelet. */
    MSQueue **inputs;     /* Bemeneti linkek táblája. */
    MSQueue **outputs;    /* Kimeneti linkek táblája. */
    struct _MSFactory *factory; /* Mutató a gyárra, amely létrehozta ezt a szűrő példányt. */
    void *padding;              /* Nem használt, ha védett mezőket adnak hozzá, akkor kerül felhasználásra. */
    void *data;                 /* Mutató egy tetszőleges struktúrára a szűrő belső állapotának és köztes számításainak tárolására. */
    struct _MSTicker *ticker;   /* Mutató az órás objektumra, amelynek nem nullának kell lennie, amikor a process() függvényt hívják. */
    /*privát attribútumok, amelyek bármikor mozgathatók és megváltoztathatók*/
    MSList *notify_callbacks; /* A szűrő eseményeinek feldolgozására használt visszahívások listája. */
    uint32_t last_tick;       /* Az utolsó óra száma, amikor a process() hívás történt. */
    MSFilterStats *stats;     /* A szűrő statisztikája.*/
    int postponed_task; /*Elhalasztott feladatok száma. Egyes szűrők elhalaszthatják az adatok feldolgozását (a process() hívását) több óra alatt.*/
    bool_t seen;  /* A zászló, amelyet a ticker használ, hogy jelölje, hogy ezt a szűrő példányt már kezelte az adott órában.*/
};
typedef struct _MSFilter MSFilter;

Miután a C-programunkban a szűrőket a szándékunk szerint összekapcsoltuk (de nem csatlakoztattuk a ticert), ezzel egy irányított gráfot hoztunk létre, amelynek csúcsai a struktúra példányai, MSFilter, az élek pedig a linkek példányai. MSQueue.

A ticker mögötti tevékenység

Amikor azt mondtam, hogy a ticker egy ütemezett szűrő, akkor nem mondtam el róla mindent. A ticker egy objektum, amely fércel a funkciók elindítására. process() a kapcsolódó szűrők mindegyikének feldolgozásához. Amikor a C-programban csatlakoztatjuk a ticert a gráf szűrőjéhez, akkor megmutatjuk a ticert a gráf, amelyet ezután irányítani fog, amíg ki nem kapcsoljuk. A csatlakoztatás után a ticker elkezdi átvizsgálni a rábízott gráfot, listát készítve az abban szereplő szűrőkről. A kettős megszámolás elkerülése érdekében jelöli a felfedezett szűrőket, beállítva az ezekben található márkát seen. A keresés az egyes szűrők linkjeit tartalmazó táblákban történik.

A bemutató túrája során a ticker ellenőrzi, hogy van-e a szűrők között legalább egy, amely adatblokk-forrásként működik. Ha nincs ilyen, akkor a gráf hibásnak minősül, és a ticker vészhelyzetben leáll.

Ha a gráf "helyesnek" bizonyult, akkor minden megtalált szűrő inicializálásához előhívják a függvényt preprocess(). Amikor elérkezik a következő feldolgozási ciklus pillanata (alapértelmezett esetben 10 millisekundumonként), a ticker előhívja a függvényt process() minden korábban megtalált forrásszűrő számára, majd a lista további szűrőire is. Ha a szűrőnek vannak belépő linkjei, akkor a függvény meghívása process() megismétlődik, amíg a belépő linkek sorai ki nem ürülnek. Ezt követően a következő szűrőre lép, és "elforgatja" azt, amíg a belépő linkek üzenetei el nem tűnnek. A ticker szűrőről szűrőre halad, amíg a lista véget nem ér. Ezen a ponton zárul le a ciklus feldolgozása.

Most térjünk vissza a tuple-ökhöz, és beszéljünk arról, miért került be egy ilyen entitás a médiastreamerbe. Általánosan elmondható, hogy az algoritmus számára szükséges adatmennyiség, amely a szűrőn belül működik, nem egyezik meg, és nem osztható meg a bejövő adatok pufferének méretével. Például írnunk kell egy szűrőt, amely gyors Fourier-transzformációt végez, ami miatt csak olyan adatblokkokat képes kezelni, amelyek mérete a kettes hatványai. Legyen ez 512 minta. Ha az adatokat telefoncsatorna generálja, akkor a bejövő üzenetek adatpufferének mérete 160 minta jelet szolgáltat számunkra. Kísértésbe eshetünk, hogy ne vegyük le az adatokat a bemenetről, amíg elérjük a szükséges mennyiségű adatot. De ebben az esetben ütközés keletkezik a tickeren, amely eredménytelenül próbálja meg elforgatni a szűrőt az belépő link kiürüléséig. Korábban ezt a szabályt a szűrő munkafolyamatának harmadik elvének neveztük. E szerint az elv szerint a szűrő process() függvényének el kell vinnie az összes adatot a bemeneti sorokból.

Ezen kívül a bemenetről csak 512 adatot nem lehet kivenni, mivel az adatokat csak egész blokkokban lehet megszerezni, azaz a szűrőnek 640 adatot kell megszereznie, és az ezekből felhasznált 512 mellett a hátralévő részt új adatok gyűjtéséhez. Így a szűrőnknek, a fő feladata mellett, segédfeladatokat is kell biztosítania a bemeneti adatok átmeneti tárolásához. A médiastreamer fejlesztői ezért kifejlesztették a speciális objektumot – MSBufferizer (pufferelő), amely ezt a feladatot tuple-ök segítségével oldja meg.

Pufferelő (MSBufferizer)

Ez az objektum az adatokat felhalmozza a szűrő belsejében, és elkezdi azokat visszaadni a feldolgozásra, amint az információ mennyisége elegendő a szűrő algoritmusának végrehajtásához. Amíg a pufferelő adatokat gyűjt, a szűrő üresjáratban működik, nem terheli a processzor számítási teljesítményét. De amint a pufferelő olvasási funkciója nem nulla értéket ad vissza, a szűrő process() funkciója elkezdi az adatokat puffereli a szükséges méretű adagokban, a felhasználásukig.
A még nem használt adatok a pufferelőben maradnak, mint a tuple első eleme, amelyhez a későbbi bemeneti adatblokkok csatlakoznak.

A pufferelő leíró struktúrája:

struct _MSBufferizer{
queue_t q; /* Üzenetküldő sor. */
int size; /* Jelenleg a pufferelőben lévő adatok összege. */
};
typedef struct _MSBufferizer MSBufferizer;

MSBufferizer-rel kapcsolatos funkciók

Új pufferelő példány létrehozása:

MSBufferizer * ms_bufferizer_new(void);

Memória allokálódik, inicializálódik a ms_bufferizer_init() és a mutató visszatér.

Inicializáló funkció:

void ms_bufferizer_init(MSBufferizer *obj); 

Az üzenetsor inicializálva q, mező méret nullára állítódik.

Üzenet hozzáadása:

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

Az m üzenet hozzáadódik a sorhoz. A kiszámított adatmennyiség hozzáadódik a méret.

Minden üzenet sorba helyezése a linken a pufferelőbe q:

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

Üzenet átvitel a linken q a pufferelőbe a ms_bufferizer_put().

funkció segítségével történik.

Olvasás a pufferelőből: 

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen);Ha a pufferelőben felhalmozott adatok mérete kisebb, mint a kért (), akkor a funkció nulla értéket ad vissza, az adatok másolása a data-ba nem történik meg. Ellenkező esetben az adatok sorozatos másolása végbemegy a pufferekben lévő tuple-ökből. A másolás befejeződik, amikor datalen bájt másolása történik meg. Ha a hely elfogy a data blokk közepén, akkor ebben az üzenetben a data blokk a még át nem másolt részre lesz lerövidítve. A következő hívásnál a másolás ettől a helytől folytatódik.

Az aktuálisan a puffert tartalmazó adatok száma:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Visszaadja a mezőt méret a puffertől.

Az aktuálisan a bufert tartalmazó adatok egy részének eldobása:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

A megadott mennyiségű bájt kinyerésre kerül és eldobásra. A legrégebbi adatok lesznek eldobva.

A puffert tartalmazó összes üzenet törlése:

void ms_bufferizer_flush(MSBufferizer *obj); 

Az adatszámláló nullára állítódik.

A puffert tartalmazó összes üzenet törlése:

void ms_bufferizer_uninit(MSBufferizer *obj); 

A számláló nullázása nem történik meg.

A buffer eltávolítása és a memória felszabadítása:

void ms_bufferizer_destroy(MSBufferizer *obj);  

A buffert használó példák a médiastreamer néhány szűrőjének forráskódjában találhatók. Például az MS_L16_ENC szűrő, ami a bájtok átrendezését végzi a hálózati sorrendből a gazdagép sorrendjébe: l16.c

A következő cikkben a ticker terhelésének értékelésével és a médiastreamerben jelentkező túlzott számítási terhelés kezelésének módjaival foglalkozunk.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster