Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11

Materiali i artikujt është marrë nga kanali im Dzen.

Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11

Mekanizmi i transferit të të dhënave

  • Blloku i tĂ« dhĂ«nave dblk_t
  • Mesazhi mblk_t
  • Funksionet e punĂ«s me mesazhet mblk_t
  • RradhĂ« queue_t
  • Funksionet e punĂ«s me rradhĂ«t queue_t
  • Lidhja e filtrave
  • Pika e sinjalizimit nĂ« grafikun e pĂ«rpunimit tĂ« tĂ« dhĂ«nave
  • Aktiviteti pas skenĂ«s i ticker-it
  • Bufferi (MSBufferizer)
  • Funksionet e punĂ«s me MSBufferizer

Në të kaluarën artikulli ynë Ne kemi zhvilluar filtrin tonë. Ky artikull do të përqendrohet në strukturen e mekanizmit të transferit të të dhënave midis filtreve të mediastream-it. Kjo do të lejojë zhvillimin e filtrave më të sofistikuar me përpjekje më të vogla.

Mekanizmi i transferit të të dhënave

Transferi i tĂ« dhĂ«nave nĂ« mediastream bĂ«het pĂ«rmes rradhĂ«ve tĂ« pĂ«rshkruara nga struktura queue_t. NĂ«pĂ«rmjet rradhĂ«ve kalojnĂ« seri mesazhesh tĂ« tipit mblk_t, tĂ« cilat nuk pĂ«rmbajnĂ« tĂ« dhĂ«na sinjali, por vetĂ«m referenca pĂ«r mesazhin e mĂ«parshĂ«m, mesazhin e ardhshĂ«m dhe bllokun e tĂ« dhĂ«nave. PĂ«r mĂ« tepĂ«r, dua tĂ« theksoj se ka edhe njĂ« fushĂ« qĂ« referon nĂ« njĂ« mesazh tĂ« kĂ«tij lloji, e cila lejon organizimin e njĂ« liste tĂ« lidhur tĂ« mesazheve. Grupin e mesazheve tĂ« bashkuara nĂ« kĂ«tĂ« listĂ« do ta quajmĂ« tuple. KĂ«shtu, çdo element i rradhĂ«s mund tĂ« jetĂ« njĂ« mesazh individual mblk_t, ose mund tĂ« jetĂ« edhe krye i tuple-ve tĂ« mesazheve mblk_t. Çdo mesazh i tuple-ve mund tĂ« ketĂ« bllokun e tij tĂ« dhĂ«nave. Pse na nevojiten tuple-t do tĂ« diskutojmĂ« pak mĂ« vonĂ«.

Siç u tha mĂ« sipĂ«r, mesazhi vetĂ« nuk pĂ«rmban bllok tĂ« dhĂ«nash, pĂ«rkundrazi ai pĂ«rmban vetĂ«m njĂ« tregues pĂ«r zonĂ«n e memorie ku ruhet blloku. NĂ« kĂ«tĂ« pjesĂ«, pamja e pĂ«rgjithshme e punĂ«s sĂ« mediastream-it ngjan me magazinĂ«n e dyerve nĂ« kartonĂ«n "Korporata e monstave", ku dyert (referencat nĂ« tĂ« dhĂ«na — dhomat) lĂ«vizin me njĂ« shpejtĂ«si tĂ« çmendur nĂ«pĂ«r banda transportuese, ndĂ«rsa vetĂ« dhomat mbeten tĂ« palĂ«vizshme.

Tani, duke u ngritur në hierarki nga poshtë lart, do të shqyrtojmë në detaje entitetet e përmendura të mekanizmit të transferit të të dhënave në mediastream.

Blloku i të dhënave dblk_t

Blloku i të dhënave përbëhet nga një krye dhe një bufer të dhënash. Kryesori përshkruhet nga struktura e mëposhtme,

typedef struct datab
{
unsigned char *db_base; // Tregues për fillimin e buferit të të dhënave.
unsigned char *db_lim;  // Tregues për fundin e buferit të të dhënave.
void (*db_freefn)(void*); // Funksioni i lirimit të memories gjatë fshirjes së bllokut.
int db_ref; // Numri i referencave.
} dblk_t;

Fushat e struktorit pĂ«rmbajnĂ« tregues pĂ«r fillimin e buffers, pĂ«rfundimin e buffers, dhe funksionin pĂ«r tĂ« fshirĂ« buffers tĂ« tĂ« dhĂ«nave. Elementi pĂ«rfundimtar nĂ« titull db_ref — numri i referencave, nĂ«se arrin zero, kĂ«tĂ« e shĂ«non si sinjal pĂ«r tĂ« fshirĂ« kĂ«tĂ« bllok nga memoria. NĂ«se blloku i tĂ« dhĂ«nave Ă«shtĂ« krijuar nga funksioni datab_alloc() , atĂ«herĂ« bufferi i tĂ« dhĂ«nave do tĂ« vendoset nĂ« memorie menjĂ«herĂ« pas titullit. NĂ« tĂ« gjitha rastet e tjera, bufferi mund tĂ« ndodhet nĂ« ndonjĂ« vend tjetĂ«r. NĂ« bufferin e tĂ« dhĂ«nave do tĂ« vendosen sinjalizime ose tĂ« dhĂ«na tĂ« tjera qĂ« dĂ«shirojmĂ« t'i pĂ«rpunojmĂ« me filtre.

Një ekzemplar i ri i bllokut të të dhënave krijohet me funksionin:

dblk_t *datab_alloc(int size);

Si njĂ« parametĂ«r hyrĂ«s, i kalon madhĂ«sinĂ« e tĂ« dhĂ«nave qĂ« do tĂ« ruajĂ« blloku. Memoria ndahet mĂ« shumĂ«, nĂ« mĂ«nyrĂ« qĂ« nĂ« fillim tĂ« memories sĂ« ndarĂ« tĂ« vendoset titulli — struktura datab. Por kur pĂ«rdoren funksione tĂ« tjera kjo nuk ndodh gjithmonĂ«, nĂ« disa raste bufferi i tĂ« dhĂ«nave mund tĂ« ndodhet veçmas nga titulli i bllokut tĂ« tĂ« dhĂ«nave. Fushat e strukturĂ«s gjatĂ« krijimit rregullohen nĂ« mĂ«nyrĂ« qĂ« fusha e saj db_base tĂ« tregojĂ« pĂ«r fillimin e zonĂ«s sĂ« tĂ« dhĂ«nave, dhe db_lim pĂ«rfundimin e saj. Numri i referencave db_ref vendoset nĂ« njĂ«. Treguesi i funksionit pĂ«r pastrimin e tĂ« dhĂ«nave vendoset nĂ« zero.

Mesazhi mblk_t

Siç u tha, elementët e radhës kanë tipin mblk_t, është përcaktuar si më poshtë:

typedef struct msgb
{
  struct msgb *b_prev;   \/\/ Treguesi për elementin e mëparshëm të listës.
  struct msgb *b_next;   \/\/ Treguesi për elementin e ardhshëm të listës.
  struct msgb *b_cont;   \/\/ Treguesi për të lidhur mesazhe të tjera me mesazhin, për të krijuar një tuple mesazhesh.
  struct datab *b_datap; \/\/ Treguesi për strukturën e bllokut të të dhënave.
  unsigned char *b_rptr; \/\/ Treguesi për fillimin e zonës së të dhënave për të lexuar të dhënat e bufferit b_datap.
  unsigned char *b_wptr; \/\/ Treguesi për fillimin e zonës së të dhënave për të shkruar të dhënat e bufferit b_datap.
  uint32_t reserved1;    \/\/ Fushe e rezervuar1, mediastreamer vendos informacion shërbimi aty. 
  uint32_t reserved2;    \/\/ Fushe e rezervuar2, mediastreamer vendos informacion shërbimi aty.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktura mblk_t në fillim përmban treguesit b_prev, b_next, të cilët janë të nevojshëm për organizimin e një liste dy-foljesh (cila është radha queue_t).

Pastaj vjen treguesi b_cont, i cili përdoret vetëm kur mesazhi hyn në tuplë. Për mesazhin e fundit në tuplë, ky tregues mbetet zero.

Më pas shohim një tregues për bllokun e të dhënave b_datap, për të cilin ekziston mesazhi. Pas tij vijnë treguesit në zonën brenda buffer-it të bllokut. Fusha b_rptr tregon vendin nga ku do të lexohen të dhënat nga buffer-i. Fusha b_wptr tregon vendin nga ku do të shkruhen të dhënat në buffer.

Fushat e mbetura janë funksionale dhe nuk lidhen me funksionimin e mekanizmit të transferimit të të dhënave.

Më poshtë tregohet një mesazh i vetëm me emrin m1 dhe bllokun e të dhënave d1.
Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11
Në figurën e ardhshme është ilustruar një tuplë e tre mesazheve m1, m1_1, m1_2.
Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11

Funksionet e punës me mesazhet mblk_t

Një mesazh i ri mblk_t krijohet nga funksioni:

mblk_t *allocb(int size, int pri); 

ai alokon nĂ« memorie njĂ« mesazh tĂ« ri mblk_t me bllok tĂ« dhĂ«nash tĂ« madhĂ«sisĂ« sĂ« caktuar size, argumenti i dytĂ« — pri nuk pĂ«rdoret nĂ« versionin e shqyrtuar tĂ« bibliotekĂ«s. Ai duhet tĂ« mbetet zero. GjatĂ« punĂ«s sĂ« funksionit do tĂ« alokohet memorie pĂ«r strukturĂ«n e mesazhit tĂ« ri dhe do tĂ« thirret funksioni mblk_init(), i cili do tĂ« zerojĂ« tĂ« gjitha fushat e instancĂ«s sĂ« krijuar tĂ« strukturĂ«s dhe mĂ« pas, pĂ«rmes datab_alloc(), do tĂ« krijojĂ« buffer-in e tĂ« dhĂ«nave. Pas kĂ«saj do tĂ« kryhet konfigurimi i fushave nĂ« strukturĂ«:

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

Në dalje marrim një mesazh të ri me fusha të inicializuara dhe një buffer të të dhënave bosh. Për të shtuar të dhëna në mesazh, duhet të kryhet kopjimi i tyre në buffer-in e bllokut të të dhënave:

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

ku data — treguesi nĂ« burimin e tĂ« dhĂ«nave, dhe size — madhĂ«sia e tyre.
Më pas duhet të përditësohet treguesi për pikën e shkruar, në mënyrë që të tregonte përsëri për fillimin e zonës së lirë në buffer:

msg->b_wptr = msg->b_wptr + size

Nëse kërkohet të krijohet një mesazh nga një buffer ekzistues, pa kopjim, atëherë për këtë përdoret funksioni:

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

Funksioni, pas krijimit të mesazhit dhe strukturës së bllokut të të dhënave, do të konfigurojë treguesit e saj në të dhëna në adresën buf. Pra, në këtë rast, buffer-i i të dhënave nuk ndodhet pas fushave të titullit të bllokut të të dhënave, siç ishte krijimi i bllokut të të dhënave nga funksioni. datab_alloc()Buferi i dërguar funksionit do të mbetet aty ku ishte, por me ndihmën e treguesve do të stimulohet për t'u lidhur me titullin e sapo krijuar të bllokut të të dhënave, dhe ai me radhë me mesazhin.

Për një mesazh mblk_t mund të lidhen disa blloqe të dhënash në mënyrë renditëse. Kjo bëhet me funksionin:

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

mp — mesazhi tĂ« cilit do t'i shtohet njĂ« bllok tjetĂ«r tĂ« dhĂ«nash;
data — treguesi pĂ«r bllokun, kopja e tĂ« cilit do tĂ« shtohet nĂ« mesazh;
size — madhĂ«sia e tĂ« dhĂ«nave;
pad — flaga qĂ« tregon se madhĂ«sia e memories e ndarĂ« duhet tĂ« jetĂ« e rregulluar nĂ« kufirin e 4 bajtĂ«ve (plotesimi do tĂ« kryhet me ose).

Nëse në buferin ekzistues të të dhënave të mesazhit ka mjaft hapësirë, atëherë të dhënat e reja do të ngjiten pas të dhënave ekzistuese atje. Nëse hapësira e lirë në buferin e të dhënave të mesazhit është më e vogël se size, atëherë krijohet një mesazh i ri, me madhësinë e mjaftueshme të buferit dhe të dhënat kopjohen në buferin e tij. Ky mesazh i ri lidhet me origjinalin përmes treguesit b_cont. Në këtë rast, mesazhi kthehet në një tuple.

Nëse në tuple duhen shtuar një bllok tjetër të dhënash, atëherë duhet të përdoret funksioni:

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

ai do të gjejë mesazhin e fundit në tuple (ai do të ketë b_cont zero) dhe do të thërrasë për këtë mesazh funksionin appendb().

Mund të dihet madhësia e të dhënave në mesazh ose tuple me anë të funksionit:

int msgdsize(const mblk_t *mp);

ai do të kalojë nëpër të gjitha mesazhet e tuple dhe do të kthejë shumën e përgjithshme të të dhënave në buferat e të dhënave të atyre mesazheve. Për çdo mesazh, sasia e të dhënave llogaritet kështu:

 mp->b_wptr - mp->b_rptr

Për të bashkuar dy tuples, përdoret funksioni:

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

ai lidh tuple-në newm në fund të tuple-së mp dhe kthen treguesin në mesazhin e fundit të tuple-së së krijuar.

Nëse është e nevojshme, tuple mund të kthehet në një mesazh të vetëm me një bllok të vetëm të të dhënash, kjo bëhet me funksionin:

void msgpullup(mblk_t *mp,int len);

nëse argumenti len është -1, atëherë madhësia e buferit të ndarë përcaktohet automatikisht. Nëse len Nëse numri është pozitiv, do të krijohet një buffer i këtij madhësie dhe të dhënat e mesazheve të tupleve do të kopjohen në të. Nëse buffer-i përfundon, kopjimi do të ndalet këtu. Mesazhi i parë i tupleve do të marrë një buffer me madhësi të re me të dhënat e kopjuara. Mesazhet e tjera do të fshihen, dhe memoria do të kthehet në kupë.

Gjatë fshirjes së strukturës mblk_t merren parasysh numri i referencave të bllokut të të dhënave, nëse kur thirret freeb() ai është baraz me zero, atëherë buffer-i i të dhënave fshihet së bashku me instancën mblk_t, që e tregon atë.

Inicijalizimi i fushave të mesazhit të ri:

void mblk_init(mblk_t *mp);

Shtimi në mesazh një tjetër sërë të dhënash:

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

Nëse të dhënat e reja nuk hyjnë në hapësirën e lirë të buffer-it të të dhënave të mesazhit, atij i bashkëngjitet një mesazh i krijuar veçmas me një buffer të nevojshëm (në mesazhin e parë vendoset një tregues në mesazhin e shtuar) dhe mesazhi shndërrohet në tuple.

Shtimi i një sërë të dhënash në tuple:

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

Funksioni thërret appendb() në një cikël.

Bashkimi i dy tupleve në një:

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

Mesazhi newm do të bashkëngjitet me mp.

Krijimi i një kopjeje të një mesazhi të vetëm:

mblk_t *copyb(const mblk_t *mp);

Kopjimi i plotë i tupleve me të gjitha blloqet e të dhënave:

mblk_t *copymsg(const mblk_t *mp);

Elementet e tupleve kopjohen nga funksioni copyb().

Krijimi i një kopjeje të lehtë të mesazhit. mblk_tNë këtë rast, blloku i të dhënave nuk kopjohet, por rritet numri i referencave të tij. db_ref:

mblk_t *dupb(mblk_t *mp);

Krijimi i një kopjeje të lehtë të tupleve. Blloqet e të dhënave nuk kopjohen, vetëm numrat e referencave të tyre rriten. db_ref:

mblk_t *dupmsg(mblk_t* m);

Ngjitja e të gjitha mesazheve të tupleve në një mesazh:

void msgpullup(mblk_t *mp,size_t len);

Nëse argumenti len është baraz me -1, atëherë madhësia e buffer-it të rezervuar përcaktohet automatikisht.

Fshirja e mesazhit, tuple:

void freemsg(mblk_t *mp);

Numri i referencave të bllokut të të dhënave zvogëlohet me njësinë. Nëse arrihet zero, atëherë blloku i të dhënave gjithashtu fshihet.

Numërimi i volumit të përgjithshëm të të dhënave në mesazh ose tuple.

size_t msgdsize(const mblk_t *mp);

Nxjerrja e mesazhit nga fundi i radhës:

mblk_t *ms_queue_peek_last (q);

Kopjimi i përmbajtjes së fushave të rezervuara nga një mesazh në një tjetër (në të vërtetë këto fusha përmbajnë flamuj që përdoren nga mediastreamer):

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

Queue queue_t

NjĂ« radhĂ« mesazhe nĂ« median streaming Ă«shtĂ« realizuar si njĂ« listĂ« dyfishe lidhĂ«se circular. Çdo element i listĂ«s pĂ«rmban njĂ« tregues nĂ« njĂ« bllok tĂ« dhĂ«nash me matje sinjali. Kjo do tĂ« thotĂ« se lĂ«vizin vetĂ«m treguesit nĂ« bllokun e tĂ« dhĂ«nave, ndĂ«rsa vetĂ« tĂ« dhĂ«nat mbeten tĂ« palĂ«vizshme. Pra, lĂ«vizin vetĂ«m lidhjet e tyre.
Struktura që përshkruan radhën queue_t, e paraqitur më poshtë:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Elementi i ngrirë" i radhës, nuk tregon për të dhëna, përdoret vetëm për të menaxhuar radhën. Gjatë inicializimit të radhës (qinit()) treguesit e saj konfigurohen në mënyrë që të tregojnë për veten e saj. */
   int q_mcount;        // Numri i elementeve në radhë.
} queue_t;

Struktura pĂ«rmban njĂ« fushĂ« — njĂ« tregues _q_stopper tĂ« tipit *mblk_t, ai tregon pĂ«r elementin e parĂ« (mesazhin) nĂ« radhĂ«. Fusha e dytĂ« e strukturĂ«s Ă«shtĂ« numri i mesazheve qĂ« ndodhen nĂ« radhĂ«.
Në figurën më poshtë është paraqitur radhë me emrin q1, që përmban 4 mesazhe m1,m2, m3, m4.
Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11
Në figurën tjetër është paraqitur radhë me emrin q1, që përmban 4 mesazhe m1,m2, m3, m4. Mesazhi m2 është krye i tuple, në të cilin përfshihen edhe dy mesazhe m2_1 dhe m2_2.

Po studiojmë motorin VoIP Mediastreamer2. Pjesa 11

Funksionet e punës me rradhët queue_t

Inicializimi i radhës:

void qinit(queue_t *q);

Fusha _q_stopper (duke e quajtur më tej "stop") inicializohet nga funksioni mblk_init(), treguesi i elementit të kaluar dhe treguesi i elementit të ardhshëm konfigurohen që të tregojnë për veten e tij. Numri i elementeve në radhë zerohet.

Shtimi i një elementi të ri (mesazhi):

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

Elementi i ri m shtohet në fund të listës, treguesit e elementit konfigurohen që të bëjnë që stopi të jetë elementi i ardhshëm për të, dhe ai për stopin elementin e kaluar. Numri i elementeve në radhë rritet.

Shkarkimi i një elementi nga radhë:

mblk_t * getq(queue_t *q); 

shkarkohet ai mesazh që ndodhet pas stopit, numri i elementeve zvogëlohet. Nëse në radhë nuk ka elemente përveç stopit, kthehet 0.

Vendosja e një mesazhi në radhë:

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

Elementi mp vendoset përpara elementit emp. Nëse emp=0, atëherë mesazhi shtohet në fund të radhës.

Shkarkimi i mesazhit nga krye i radhës:

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

Numri i elementeve zvogëlohet.

Leximi i treguesit në elementin e parë të radhës:

mblk_t * peekq(queue_t *q); 

Fshirja e të gjitha elementeve nga radhë me fshirjen e elementeve vetë:

void flushq(queue_t *q, int how);

Argumenti how nuk është në përdorim. Kontatori i elementeve në radhë vendoset në zero.

Makron për të lexuar treguesin në elementin e fundit të radhës:

mblk_t * qlast(queue_t *q);

Kur punoni me radhët e mesazheve, duhet të keni parasysh se kur thirret ms_queue_put(q, m) me një tregues null për mesazhin, funksioni bllokohet. Programi juaj do të ngecë. Po ashtu sillet ms_queue_next(q, m).

Lidhja e filtrave

Radhët e përshkruara më sipër përdoren për të transmetuar mesazhe nga një filtrues në një tjetër ose nga një në disa filtrues. Filtruesit dhe lidhjet midis tyre formojnë një grafik drejtues. Hyrja ose dalja e filtruesit do të quhet me fjalën e përgjithshme "pin". Për të përshkruar rendin e lidhjeve midis filtruesve, në mediastreamer përdoret koncepti "pikë sinjali". Pika sinjali është një strukturë _MSCPoint, e cila përmban një tregues në filtrin dhe një numër të njërit prej pinëve të tij, përkatësisht ajo përshkruan lidhjen e njërës prej hyrjeve ose daljeve të filtrit.

Pika e sinjalizimit në grafikun e përpunimit të të dhënave

typedef struct _MSCPoint{
struct _MSFilter *filter; // Tregues në filtrin e mediastreamer.
int pin; // Numri i njërës prej hyrjeve ose daljeve të filtrit, dmth. pin.
} MSCPoint;

Pinat e filtruesve numërohen duke filluar nga zero.

Lidhja midis dy pinave me një radhë mesazhesh përshkruhet nga struktura _MSQueue, e cila përmban një radhë mesazhesh dhe tregues për dy pika sinjali, të cilat ajo i lidh:

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

Do ta quajmĂ« kĂ«tĂ« strukturĂ« lidhje sinjali. Çdo filtrues mediastreamer, pĂ«rmban njĂ« tabelĂ« tĂ« lidhjeve hyrĂ«se dhe njĂ« tabelĂ« tĂ« lidhjeve dalĂ«se (MSQueue). MadhĂ«sia e tabelave pĂ«rcaktohet gjatĂ« krijimit tĂ« filtrit, ne e kemi bĂ«rĂ« kĂ«tĂ« tashmĂ« me anĂ« tĂ« njĂ« variabli eksporti tĂ« tipit MSFilterDesc, kur zhvilluam filtrin tonĂ« tĂ« vetin. MĂ« poshtĂ« tregohet struktura qĂ« pĂ«rshkruan çdo filtrues nĂ« mediastreamer, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    
    /* PĂ€rja e filtrit. */
    /* Atributet e mbrojtura, ato nuk mund të lëvizin ose të hiqen, ndryshe do të prishen funksionet me pluginët. */
    ms_mutex_t lock;      /* Semafori. */
    MSQueue **inputs;     /* Tabela e lidhjeve hyrëse. */
    MSQueue **outputs;    /* Tabela e lidhjeve dalëse. */
    struct _MSFactory *factory; /* PÀrja e fabrikës që krijoi këtë instancë filtri. */
    void *padding;              /* Nuk përdoret, do të angazhohet nëse shtohen fusha të mbrojtura. */
    void *data;                 /* PÀrja e strukturës së rastit për ruajtjen e të dhënave të gjendjes brendshme të filtrit dhe llogaritjeve ndërmjetëse. */
    struct _MSTicker *ticker;   /* PÀrja e objektit ticker, i cili nuk duhet të jetë zero kur thirret funksioni process(). */
    /* atribute private, ato mund të lëvizin dhe të ndryshohen në çdo kohë */
    MSList *notify_callbacks; /* Lista e thirrjeve kthese, të përdorura për trajtimin e ngjarjeve të filtrit. */
    uint32_t last_tick;       /* Numri i ciklit të fundit kur u thirr process(). */
    MSFilterStats *stats;     /* Statistika e punës së filtrit. */
    int postponed_task; /* Numri i detyrave të shtyra. Disa filtra mund të vonojnë përpunimin e të dhënave (thirrja process()) për disa cikle. */
    bool_t seen;  /* Flagu që përdor ticker për të shënuar se ky instancë filtri është trajtuar në këtë cikël. */
};
typedef struct _MSFilter MSFilter;

Pasi që ne në programin C lidhëm filtrat sipas konceptit tonë (por nuk lidhi ticker-in), ne kështu krijuam një graf të orientuar, ku nyjet janë instancat e strukturës MSFilter, dhe skajet janë instancat e lidhjeve MSQueue.

Aktiviteti pas skenës i ticker-it

Kur ju thashë se ticker-i është filtri burim i cikleve, nuk ishte e gjitha e vërteta rreth tij. Ticker-i është një objekt që me orë ekzekuton përmbledhjen e funksioneve process() të të gjithë filtrave të skemës (grafit) me të cilin është i lidhur. Kur ne në programin C lidhi ticker-in me filtrin e grafit, ne i tregojmë ticker-it grafit që nga ky moment do ta menaxhojë, derisa të shkëputim lidhjen. Pas lidhjes, ticker-i fillon të shqyrtojë grafit që i është besuar, duke përpiluar një listë filtrash që përfshihen në të. Për të mos "numëruar" të njëjtin filtër dy herë, ai shënon filtrat e zbuluar, duke vendosur në to një flamur seen. Kërkimi bëhet përmes tabelave të lidhjeve që ka çdo filtër.

Gjatë ekskursioneve të tij hyrëse në grafik, ticker kontrollon nëse ka të paktën një filtrues nga filtrat që plotëson rolin e burimit të blloqeve të të dhënave. Nëse nuk ka, grafiku konsiderohet i pasaktë dhe tickert e mbyll punën në mënyrë emergjente.

Nëse grafiku del "i saktë", për çdo filtrues të gjetur, për inicializim, thirret funksioni preprocess(). Pasi të arrijë momenti për ciklin e ardhshëm të përpunimit (në mënyrë default çdo 10 milisekonda), ticker thërret funksionin process() për të gjithë filtrat burim të gjetur më parë, dhe pastaj për filtrat e tjerë në listë. Nëse filtri ka lidhje hyrëse, atëherë ekzekutimi i funksionit process() përsëritet derisa radhët e lidhjeve hyrëse të zbrazin. Pas kësaj, ai kalon në filtrin e ardhshëm në listë dhe "rrotullon" atë deri në çlirimin e lidhjeve hyrëse nga mesazhet. Ticker kalon nga filtri në filtru derisa lista të mbarojë. Këtu përfundon cikli i përpunimit.

Tani, do të kthehemi te tuple dhe do të flasim për arsye se përse një entitet i tillë u shtua në streamerin multimedia. Në rastin e përgjithshëm, sasia e të dhënave e nevojshme për algoritmin që funksionon brenda filtrit nuk përputhet dhe nuk është shumëfish i madhësisë së bufeve të të dhënave që vijnë në hyrje. Për shembull, ne shkruajmë një filtru që kryen një transformim të shpejtë të Fourier-it, i cili sipas përkufizimit mund të përpunojë vetëm blloqe të dhënash me madhësi që janë fuqi e dyshës. Le të jetë ky 512 mostra. Nëse të dhënat gjenerohen nga një kanal telefoni, atëherë buffer-i i të dhënave për çdo mesazh në hyrje do të na sjellë 160 mostra të sinjalit. Ka një tundim për të mos marrë të dhëna nga hyrja derisa të ketë numrin e nevojshëm të të dhënave. Por në këtë rast do të ndodhte një kolizion me ticker-in, i cili do të përpiqej pa sukses të rrotullonte filtrin deri sa të zbrazte lidhjen hyrëse. Më parë ne e kemi quajtur këtë rregull si parimi i tretë i funksionimit të filtrit. Sipas këtij parimi, funksioni process() i filtrit duhet të marrë të gjitha të dhënat nga radhët hyrëse.

Përveç kësaj, nga hyrja nuk do të jetë e mundur të merret vetëm 512 matje, pasi ato mund të merren vetëm në blloqe të tëra, dmth filtri do të duhet të marrë 640 matje dhe duke përdorur 512 nga to, mbetja deri në grumbullimin e një partie të re të të dhënave. Kështu, filtri ynë, përveç detyrës së tij kryesore, duhet të sigurojë veprime ndihmëse për ruajtjen ndërmjetëse të të dhënave hyrëse. Zhvilluesit e mediastream-it për këtë detyrë të përgjithshme kanë zhvilluar një objekt të veçantë - MSBufferizer (bufruesi), i cili zgjidh këtë problem me anë të tupave.

Bufferi (MSBufferizer)

Ky është një objekt që do të akumullojë të dhënat hyrëse brenda filtrit dhe do të fillojë t'i japë ato për përpunim, sa herë që sasia e informacionit të jetë e mjaftueshme për të zhvilluar algoritmin e filtrit. Ndërsa bufruesi akumulon të dhëna, filtri do të punojë në modin e zbrazët, pa shpenzuar fuqinë llogaritëse të procesorit. Por sapo funksioni i leximit nga bufruesi kthehet me një vlerë të ndryshme nga zero, funksioni process() i filtrit fillon të marrë dhe të përpunojë të dhënat nga bufruesi në porcione të nevojshme, deri në shterimin e tyre.
Të dhënat e papërdorura mbeten në bufrues si elementi i parë i tupës, të cilit i bashkohen blloqet e tjera të të dhënave hyrëse.

Struktura që përshkruan bufruesin:

struct _MSBufferizer{
queue_t q; /* Rreshti i mesazheve. */
int size; /* Shuma totale e të dhënave në bufrues në këtë moment. */
};
typedef struct _MSBufferizer MSBufferizer;

Funksionet e punës me MSBufferizer

Krijimi i një ekzemplar të ri të bufruesit:

MSBufferizer * ms_bufferizer_new(void);

Për allocated_memory, inicializohet në ms_bufferizer_init() dhe kthehet një tregues.

Funksioni i inicializimit:

void ms_bufferizer_init(MSBufferizer *obj); 

Inicializohet rreshti q, fusha size dhe vendoset në zero.

Shtimi i mesazhit:

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

Mesazhi m shtohet në rresht. Sasia e llogaritur e blloqeve të të dhënave shtohet në size.

Shkëmbimi në bufruesin e të gjithë mesazheve të rreshtit të të dhënave të lidhjes q:

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

Transferimi i mesazheve nga lidhja q në bufrues bëhet me anë të funksionit ms_bufferizer_put().

Leximi nga bufruesi:

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

Nëse përmasat e të dhënave të akumuluara në bufrues rezultojnë të jenë më të vogla se sa është e kërkuar (datalen), funksioni kthen zero, kopjimi i të dhënave në data nuk kryhet. Në të kundërt, kryhet kopjimi i radhës së të dhënave nga tabelat që ndodhen në bufer. Pas kopjimit, tabela fshihet dhe memoria lirohet. Kopjimi përfundon në momentin kur janë kopjuar datalen bajta. Nëse vendi mbaron në gjysmë të bllokut të të dhënave, atëherë në këtë mesazh, blloku i të dhënave do të jetë i shkurtohet në pjesën e mbetur të papërfunduar. Në thirrjen e ardhshme, kopjimi do të vazhdojë nga ky vend.

Leximi i sasisë së të dhënave që janë të disponueshme në këtë moment në bufer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Kthen fushën size e buferizatorit.

Hedhja e pjesës së të dhënave që ndodhen në bufer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Sasia e specifikuar e bajtave të të dhënave nxirret dhe hidhet. Hidhen të dhënat më të vjetra.

Shkarkimi i të gjitha mesazheve që ndodhen në bufer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Numëruesi i të dhënave resetohet në zero.

Shkarkimi i të gjitha mesazheve që ndodhen në bufer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Resetimi i numëruesit nuk kryhet.

Fshirja e buferizatorit dhe lirimi i memories:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Shembujt e përdorimit të buferizatorit mund të gjenden në kodin burimor të disa filtra të mediastreamerit. Për shembull, në filtrin MS_L16_ENC, i cili kryen rregullimin e bajtave në mostra nga rendi rrjetor në rendin e mikpritësit: l16.c

Në artikullin që vijon, do të shqyrtojmë çështjen e vlerësimit të ngarkesës në ticker dhe mënyrat e trajtimit të ngarkesës së tepruar të llogaritjes në mediastreamer.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster