Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11

Materiali i artikullit është marrë nga ime kanal zen.

Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11

Mekanizmi i lëvizjes së të dhënave

  • Blloku i të dhënave dblk_t
  • Mesazh mblk_t
  • Funksionet për të punuar me mesazhe mblk_t
  • Radha_t
  • Funksionet për të punuar me radhë queue_t
  • Lidhja e filtrave
  • Grafiku i pikës së sinjalit të përpunimit të të dhënave
  • Aktivitetet prapa skenave të tikerit
  • Bufferizer (MSBufferizer)
  • Funksionet për të punuar me MSBufferizer

Në të fundit artikull ne kemi zhvilluar filtrin tonë. Ky artikull do të fokusohet në mekanizmin e brendshëm për lëvizjen e të dhënave midis filtrave të transmetuesit të medias. Kjo do t'ju lejojë të shkruani filtra të sofistikuar me më pak përpjekje në të ardhmen.

Mekanizmi i lëvizjes së të dhënave

Lëvizja e të dhënave në transmetuesin e medias kryhet duke përdorur radhët e përshkruara nga struktura radhë_t. Vargjet e mesazheve si mblk_t, të cilat vetë nuk përmbajnë të dhëna sinjali, por vetëm lidhje me mesazhin e mëparshëm, të ardhshëm dhe në bllokun e të dhënave. Për më tepër, dua të theksoj veçanërisht se ekziston gjithashtu një fushë për një lidhje me një mesazh të të njëjtit lloj, e cila ju lejon të organizoni një listë mesazhesh të lidhura vetëm. Ne do ta quajmë një grup mesazhesh të bashkuara nga një listë e tillë një tufë. Kështu, çdo element i radhës mund të jetë një mesazh i vetëm mblk_t, dhe ndoshta kreu i një tuple mesazhi mblk_t. Çdo mesazh tuple mund të ketë bllokun e vet të të dhënave të lagjes. Ne do të diskutojmë pse tuples nevojiten pak më vonë.

Siç u përmend më lart, vetë mesazhi nuk përmban një bllok të dhënash; përkundrazi, ai përmban vetëm një tregues në zonën e kujtesës ku është ruajtur blloku. Në këtë pjesë, tabloja e përgjithshme e punës së transmetuesit të medias të kujton depon e derës në filmin vizatimor "Monsters, Inc.", ku dyert (lidhjet me të dhënat - dhomat) lëvizin me një shpejtësi të çmendur përgjatë transportuesve të sipërm, ndërsa vetë dhomat mbeten pa lëvizje.

Tani, duke lëvizur përgjatë hierarkisë nga poshtë lart, le të shqyrtojmë në detaje entitetet e listuara të mekanizmit të transmetimit të të dhënave në transmetuesin e medias.

Blloku i të dhënave dblk_t

Blloku i të dhënave përbëhet nga një kokë dhe një buffer i të dhënave. Kreu përshkruhet nga struktura e mëposhtme,

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

Fushat e strukturës përmbajnë tregues për fillimin e buferit, fundin e buferit dhe funksionin për fshirjen e buferit të të dhënave. Elementi i fundit në kokë db_ref — numëruesi i referencës, nëse arrin zero, ky shërben 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ë buferi i të dhënave do të vendoset në memorie menjëherë pas kokës. Në të gjitha rastet e tjera, buferi mund të vendoset diku veçmas. Buferi i të dhënave do të përmbajë mostra sinjalesh ose të dhëna të tjera që duam t'i përpunojmë me filtra.

Një shembull i ri i një blloku të dhënash krijohet duke përdorur funksionin:

dblk_t *datab_alloc(int size);

Si parametër hyrës, jepet madhësia e të dhënave që blloku do të ruajë. Më shumë memorie ndahet në mënyrë që të vendoset një titull - strukturë - në fillim të memories së alokuar të dhënave. Por kur përdorni funksione të tjera, kjo nuk ndodh gjithmonë; në disa raste, buferi i të dhënave mund të vendoset veçmas nga kreu i bllokut të të dhënave. Kur krijohet një strukturë, fushat janë konfiguruar në mënyrë që fusha e saj db_bazë tregoi fillimin e zonës së të dhënave, dhe db_lim deri në fund të saj. Numri i lidhjeve db_ref është vendosur në një. Treguesi i funksionit të pastrimit të të dhënave është vendosur në zero.

Mesazh mblk_t

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

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Strukturë mblk_t përmban tregues në fillim b_prev, b_tjetër, të cilat janë të nevojshme për të organizuar një listë të lidhur dyfish (e cila është një radhë radhë_t).

Pastaj vjen treguesi b_vazhdim, e cila përdoret vetëm kur mesazhi është pjesë e një tupleje. Për mesazhin e fundit në tuple, ky tregues mbetet i pavlefshëm.

Më pas shohim një tregues në një bllok të dhënash b_datap, për të cilën ekziston mesazhi. Ai ndiqet nga tregues në zonën brenda buferit të të dhënave të bllokut. Fusha b_rptr specifikon vendndodhjen nga e cila do të lexohen të dhënat nga buferi. Fusha b_wptr tregon vendndodhjen nga do të kryhen shkrimet në buffer.

Fushat e mbetura janë të një natyre shërbimi dhe nuk kanë të bëjnë me funksionimin e mekanizmit të transferimit të të dhënave.

Më poshtë është një mesazh i vetëm me emrin m1 dhe blloku i të dhënave d1.
Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11
Figura e mëposhtme tregon një tufë prej tre mesazhesh m1, m1_1, m1_2.
Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11

Funksionet e mesazheve mblk_t

Një mesazh i ri mblk_t krijuar nga funksioni:

mblk_t *allocb(int size, int pri); 

ajo vendos një mesazh të ri në kujtesë mblk_t me një bllok të dhënash të madhësisë së specifikuar madhësi, argumenti i dytë - pri nuk përdoret në këtë version të bibliotekës. Duhet të mbetet zero. Gjatë funksionimit të funksionit, memoria do të ndahet për strukturën e mesazhit të ri dhe funksioni do të thirret mblk_init(), i cili do të rivendosë të gjitha fushat e instancës së krijuar të strukturës dhe më pas, duke përdorur sa më sipër datab_alloc(), do të krijojë një bufer të dhënash. Pas së cilës do të konfigurohen fushat 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ë bufer bosh të të dhënave. Për të shtuar të dhëna në një mesazh, duhet ta kopjoni atë në buferin e bllokut të të dhënave:

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

ku të dhëna është një tregues për burimin e të dhënave, dhe madhësi - madhësia e tyre.
atëherë ju duhet të përditësoni treguesin në pikën e shkrimit në mënyrë që të tregojë përsëri në fillim të zonës së lirë në tampon:

msg->b_wptr = msg->b_wptr + size

Nëse keni nevojë të krijoni një mesazh nga një buffer ekzistues, pa kopjuar, atëherë përdorni funksionin:

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 tij për të dhënat në adresën tifoz. ato. në këtë rast, buferi i të dhënave nuk ndodhet pas fushave të kokës së bllokut të të dhënave, siç ishte rasti kur krijoni një bllok të dhënash me funksionin datab_alloc(). Buferi me të dhënat e kaluara në funksion do të mbetet aty ku ishte, por me ndihmën e treguesve do t'i bashkëngjitet kokës së sapokrijuar të bllokut të të dhënave dhe, në përputhje me rrethanat, mesazhit.

Për një mesazh mblk_t Disa blloqe të dhënash mund të lidhen në mënyrë sekuenciale. Kjo bëhet nga funksioni:

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

mp — një mesazh në të cilin do të shtohet një bllok tjetër i të dhënave;
të dhëna — treguesi i bllokut, një kopje e të cilit do t'i shtohet mesazhit;
madhësi — madhësia e të dhënave;
jastëk — një flamur që madhësia e memories së caktuar duhet të përafrohet përgjatë një kufiri prej 4 bajtësh (mbushja do të bëhet me zero).

Nëse ka hapësirë ​​të mjaftueshme në buferin ekzistues të të dhënave të mesazheve, atëherë të dhënat e reja do të ngjiten pas të dhënave që janë tashmë atje. Nëse ka më pak hapësirë ​​të lirë në buferin e të dhënave të mesazhit sesa madhësi, atëherë krijohet një mesazh i ri me një madhësi të mjaftueshme buferi dhe të dhënat kopjohen në buferin e tij. Ky është një mesazh i ri, i lidhur me atë origjinal duke përdorur një tregues b_vazhdim. Në këtë rast, mesazhi kthehet në një tufë.

Nëse duhet të shtoni një bllok tjetër të dhënash në tuple, atëherë duhet të përdorni funksionin:

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

ajo do të gjejë mesazhin e fundit në tuple (ai e ka b_vazhdim do të jetë null) dhe do të thërrasë funksionin për këtë mesazh appendb ().

Mund të zbuloni madhësinë e të dhënave në një mesazh ose tuple duke përdorur funksionin:

int msgdsize(const mblk_t *mp);

ai do të kalojë nëpër të gjitha mesazhet në tuple dhe do të kthejë sasinë totale të të dhënave në buferët e të dhënave të atyre mesazheve. Për çdo mesazh, sasia e të dhënave llogaritet si më poshtë:

 mp->b_wptr - mp->b_rptr

Për të kombinuar dy tuple, përdorni funksionin:

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

ajo shton tuplen i ri te bishti i tuples mp dhe kthen një tregues në mesazhin e fundit të tuples që rezulton.

Nëse është e nevojshme, një tuple mund të shndërrohet në një mesazh me një bllok të vetëm të dhënash; kjo bëhet nga funksioni:

void msgpullup(mblk_t *mp,int len);

nëse argumenti Len është -1, atëherë madhësia e buferit të alokuar përcaktohet automatikisht. Nëse Len është një numër pozitiv, do të krijohet një tampon i kësaj madhësie dhe të dhënat e mesazheve të dyfishta do të kopjohen në të. Nëse buferi mbaron, kopjimi do të ndalojë atje. Mesazhi i parë i tuples do të marrë një buffer me madhësi të re me të dhënat e kopjuara. Mesazhet e mbetura do të fshihen dhe kujtesa do të kthehet në grumbull.

Kur fshini një strukturë mblk_t numërimi i referencës së bllokut të të dhënave merret parasysh nëse, kur telefononi freeb () rezulton të jetë zero, atëherë buferi i të dhënave fshihet së bashku me shembullin mblk_t, e cila tregon për të.

Inicializimi i fushave të një mesazhi të ri:

void mblk_init(mblk_t *mp);

Shtimi i një pjese tjetër të të dhënave në mesazh:

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

Nëse të dhënat e reja nuk futen në hapësirën e lirë të buferit të të dhënave të mesazhit, atëherë mesazhit i bashkëngjitet një mesazh i krijuar veçmas me një bufer të madhësisë së kërkuar (një tregues për mesazhin e shtuar është vendosur në mesazhin e parë) dhe mesazhi kthehet në një tufë.

Shtimi i një pjese të të dhënave në një tuple:

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

Funksioni thërret appendb() në një lak.

Kombinimi i dy tupave në një:

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

Mesazh i ri do t'i bashkëngjiten mp.

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

mblk_t *copyb(const mblk_t *mp);

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

mblk_t *copymsg(const mblk_t *mp);

Elementet e tuples kopjohen nga funksioni copyb ().

Krijoni një kopje të lehtë të një mesazhi mblk_t. Në këtë rast, blloku i të dhënave nuk kopjohet, por numëruesi i tij i referencës rritet db_ref:

mblk_t *dupb(mblk_t *mp);

Bërja e një kopjeje të lehtë të një tufeje. Blloqet e të dhënave nuk kopjohen, vetëm numëruesit e tyre të referencës rriten db_ref:

mblk_t *dupmsg(mblk_t* m);

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

void msgpullup(mblk_t *mp,size_t len);

Nëse argumenti Len është -1, atëherë madhësia e buferit të alokuar përcaktohet automatikisht.

Fshirja e një mesazhi, dyfisho:

void freemsg(mblk_t *mp);

Numri i referencës së bllokut të të dhënave zvogëlohet me një. Nëse arrin zero, blloku i të dhënave gjithashtu fshihet.

Llogaritja e sasisë totale të të dhënave në një mesazh ose tuple.

size_t msgdsize(const mblk_t *mp);

Marrja e një mesazhi nga fundi i radhës:

mblk_t *ms_queue_peek_last (q);

Kopjimi i përmbajtjes së fushave të rezervuara të një mesazhi në një mesazh tjetër (në fakt, këto fusha përmbajnë flamuj që përdoren nga transmetuesi i medias):

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

Radhe radhë_t

Radha e mesazheve në transmetuesin e medias zbatohet si një listë rrethore e lidhur dyfish. Çdo element i listës përmban një tregues drejt një blloku të dhënash me mostra sinjalesh. Rezulton se vetëm treguesit në bllokun e të dhënave lëvizin me radhë, ndërsa vetë të dhënat mbeten të palëvizshme. ato. zhvendosen vetëm lidhjet me to.
Struktura që përshkruan radhën radhë_t, treguar me poshte:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

Struktura përmban një fushë - një tregues _q_ndalues shkruani *mblk_t, tregon elementin e parë (mesazhin) në radhë. Fusha e dytë e strukturës është numëruesi i mesazheve në radhë.
Figura më poshtë tregon një radhë të quajtur q1 që përmban 4 mesazhe m1, m2, m3, m4.
Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11
Figura e mëposhtme tregon një radhë të quajtur q1 që përmban 4 mesazhe m1,m2,m3,m4. Mesazhi m2 është koka e një tuple që përmban dy mesazhe të tjera m2_1 dhe m2_2.

Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 11

Funksionet për të punuar me radhë queue_t

Inicializimi i radhës:

void qinit(queue_t *q);

Fushë _q_ndalues (në tekstin e mëtejmë do ta quajmë "tapë") inicializohet nga funksioni mblk_init(), elementi i tij i mëparshëm dhe treguesi i elementit tjetër janë rregulluar për të treguar në vetvete. Numëruesi i elementit të radhës është rivendosur në zero.

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

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

Element i ri m shtohet në fund të listës, treguesit e elementit rregullohen në mënyrë që ndaluesi të bëhet elementi tjetër për të, dhe ai të bëhet elementi i mëparshëm për ndaluesin. Numëruesi i elementit të radhës është rritur.

Marrja e një elementi nga radha:

mblk_t * getq(queue_t *q); 

Merret mesazhi që vjen pas ndaluesit dhe numëruesi i elementit zvogëlohet. Nëse nuk ka elementë në radhë përveç ndaluesit, atëherë 0 kthehet.

Futja e një mesazhi në një radhë:

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

element mp futet përpara elementit PMM. nëse PMM=0, atëherë mesazhi shtohet në fund të radhës.

Marrja e një mesazhi nga kreu i radhës:

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

Numri i elementeve është zvogëluar.

Leximi i një treguesi për elementin e parë në radhë:

mblk_t * peekq(queue_t *q); 

Heqja e të gjithë elementëve nga radha ndërsa fshini vetë elementët:

void flushq(queue_t *q, int how);

Argument si nuk përdoret. Numëruesi i elementit të radhës është vendosur në zero.

Makro për leximin e një treguesi në elementin e fundit të radhës:

mblk_t * qlast(queue_t *q);

Kur punoni me radhë mesazhesh, kini parasysh se kur telefononi ms_radha_vendosje(q, m) me një tregues null në mesazh, funksioni hapet. Programi juaj do të ngrijë. sillet në mënyrë të ngjashme ms_radha_tjetër(q, m).

Lidhja e filtrave

Radha e përshkruar më sipër përdoret për të kaluar mesazhe nga një filtër në tjetrin ose nga një në disa filtra. Filtrat dhe lidhjet e tyre formojnë një grafik të drejtuar. Hyrja ose dalja e filtrit do të quhet fjala e përgjithshme "pin". Për të përshkruar rendin në të cilin filtrat lidhen me njëri-tjetrin, transmetuesi i medias përdor konceptin e një "pike sinjali". Pika e sinjalit është struktura _MSCPpoint, i cili përmban një tregues për filtrin dhe numrin e njërës prej kunjave të tij; në përputhje me rrethanat, ai përshkruan lidhjen e një prej hyrjeve ose daljeve të filtrit.

Grafiku i pikës së sinjalit të përpunimit të të dhënave

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

Kunjat e filtrit numërohen duke filluar nga zero.

Lidhja e dy kunjave nga një radhë mesazhesh përshkruhet nga struktura _MSQueue, e cila përmban një radhë mesazhesh dhe tregues për dy pikat e sinjalit që lidh:

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

Ne do ta quajmë këtë strukturë një lidhje sinjali. Çdo filtër i transmetimit të medias përmban një tabelë të lidhjeve hyrëse dhe një tabelë të lidhjeve dalëse (MSQueue). Madhësia e tabelave vendoset kur krijoni një filtër; ne e kemi bërë tashmë këtë duke përdorur një variabël të eksportuar të llojit MSFilterDesc, kur zhvilluam filtrin tonë. Më poshtë është një strukturë që përshkruan çdo filtër në një transmetues mediash, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Pasi lidhëm filtrat në programin C në përputhje me planin tonë (por nuk e lidhëm shenjën), në këtë mënyrë krijuam një grafik të drejtuar, nyjet e të cilit janë shembuj të strukturës MSFilter, dhe skajet janë shembuj të lidhjeve MSQueue.

Aktivitetet prapa skenave të tikerit

Kur ju thashë që tikeri është një filtër për burimin e rriqrave, nuk ishte e gjithë e vërteta për të. Tikeri është një objekt që kryen funksione në orë proces () të gjithë filtrat e qarkut (grafikut) me të cilin është lidhur. Kur lidhim një tiker me një filtër grafik në një program C, i tregojmë shenjës grafikun që do të kontrollojë tani e tutje derisa ta fikim. Pas lidhjes, tiker fillon të ekzaminojë grafikun që i është besuar kujdesit të tij, duke përpiluar një listë të filtrave që e përfshijnë atë. Për të mos "numëruar" dy herë të njëjtin filtër, ai shënon filtrat e zbuluar duke vendosur një kuti kontrolli në to. parë. Kërkimi kryhet duke përdorur tabelat e lidhjeve që ka çdo filtër.

Gjatë vizitës së tij hyrëse në grafik, treguesi kontrollon nëse midis filtrave ka të paktën një që vepron si burim i blloqeve të të dhënave. Nëse nuk ka asnjë, atëherë grafiku konsiderohet i pasaktë dhe treguesi rrëzohet.

Nëse grafiku rezulton të jetë "i saktë", për çdo filtër të gjetur, funksioni thirret për inicializim paraproces (). Sapo të arrijë momenti për ciklin tjetër të përpunimit (çdo 10 milisekonda si parazgjedhje), treguesi thërret funksionin proces () për të gjithë filtrat e burimit të gjetur më parë, dhe më pas për filtrat e mbetur në listë. Nëse filtri ka lidhje hyrëse, atëherë ekzekutoni funksionin proces () përsëritet derisa radhët e lidhjeve hyrëse të jenë bosh. Pas kësaj, ai kalon te filtri tjetër në listë dhe "lëviz" derisa lidhjet hyrëse të jenë pa mesazhe. Tikeri lëviz nga filtri në filtër derisa lista të përfundojë. Kjo përfundon përpunimin e ciklit.

Tani do të kthehemi te tuples dhe do të flasim pse një ent i tillë u shtua në transmetuesin mediatik. Në përgjithësi, sasia e të dhënave të kërkuara nga algoritmi që vepron brenda filtrit nuk përkon dhe nuk është shumëfish i madhësisë së buferëve të të dhënave të marra në hyrje. Për shembull, ne po shkruajmë një filtër që kryen një transformim të shpejtë të Furierit, i cili sipas definicionit mund të përpunojë vetëm blloqe të dhënash, madhësia e të cilëve është një fuqi prej dy. Le të jetë 512 numërime. Nëse të dhënat gjenerohen nga një kanal telefonik, atëherë buferi i të dhënave të çdo mesazhi në hyrje do të na sjellë 160 mostra sinjalesh. Është joshëse që të mos mblidhen të dhëna nga hyrja derisa të ketë sasinë e kërkuar të të dhënave. Por në këtë rast, do të ndodhë një përplasje me treguesin, i cili do të përpiqet pa sukses të lëvizë filtrin derisa lidhja e hyrjes të jetë bosh. Më parë, ne e caktuam këtë rregull si parimin e tretë të filtrit. Sipas këtij parimi, funksioni process() i filtrit duhet të marrë të gjitha të dhënat nga radhët hyrëse.

Për më tepër, nuk do të jetë e mundur të merren vetëm 512 mostra nga hyrja, pasi mund të merrni vetëm blloqe të tëra, d.m.th. filtri do të duhet të marrë 640 mostra dhe të përdorë 512 prej tyre, pjesa e mbetur përpara se të grumbullojë një pjesë të re të të dhënave. Kështu, filtri ynë, krahas punës së tij kryesore, duhet të ofrojë veprime ndihmëse për ruajtjen e ndërmjetme të të dhënave hyrëse. Zhvilluesit e transmetuesit të medias dhe zgjidhjes së këtij problemi të përgjithshëm kanë zhvilluar një objekt të veçantë - MSBufferizer (bufferer), i cili e zgjidh këtë problem duke përdorur tuples.

Bufferizer (MSBufferizer)

Ky është një objekt që do të grumbullojë të dhëna hyrëse brenda filtrit dhe do të fillojë t'i dërgojë ato për përpunim sapo sasia e informacionit të jetë e mjaftueshme për të ekzekutuar algoritmin e filtrit. Ndërsa buferi po grumbullon të dhëna, filtri do të funksionojë në modalitetin boshe, pa përdorur fuqinë përpunuese të procesorit. Por sapo funksioni i leximit nga bufereri kthen një vlerë të ndryshme nga zero, funksioni process() i filtrit fillon të marrë dhe përpunojë të dhëna nga bufereri në pjesë të madhësisë së kërkuar, derisa të shterohet.
Të dhënat që nuk kërkohen ende mbeten në tampon si elementi i parë i tuples, të cilit i bashkëngjiten blloqet pasuese të të dhënave hyrëse.

Struktura që përshkruan buferin:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Funksionet për të punuar me MSBufferizer

Krijimi i një shembulli të ri buffer:

MSBufferizer * ms_bufferizer_new(void);

Kujtesa shpërndahet, inicializohet në ms_bufferizer_init() dhe kthehet një tregues.

Funksioni i inicializimit:

void ms_bufferizer_init(MSBufferizer *obj); 

Radha po inicializohet q, fushë madhësi është vendosur në zero.

Shtimi i një mesazhi:

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

Mesazhi m shtohet në radhë. Madhësia e llogaritur e blloqeve të të dhënave i shtohet madhësi.

Transferimi i të gjitha mesazheve nga radha e të dhënave të lidhjes në buffer q:

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

Transferimi i mesazheve nga një lidhje q në bufer kryhet duke përdorur funksionin ms_bufferizer_put().

Leximi nga buferi:

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

Nëse madhësia e të dhënave të grumbulluara në bufer është më e vogël se ajo e kërkuar (datalen), më pas funksioni kthen zero, të dhënat nuk kopjohen në të dhëna. Përndryshe, kryhet kopjimi sekuencial i të dhënave nga tuplet e vendosur në buffer. Pas kopjimit, tupleja fshihet dhe kujtesa lirohet. Kopjimi përfundon në momentin kur kopjohen bajtet e të dhënave. Nëse hapësira mbaron në mes të një blloku të dhënash, atëherë në këtë mesazh, blloku i të dhënave do të reduktohet në pjesën e mbetur të pakopjuar. Herën tjetër që do të telefononi, kopjimi do të vazhdojë nga kjo pikë.

Leximi i sasisë së të dhënave që është aktualisht në dispozicion në tampon:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

E kthen fushën madhësi tampon.

Heqja e një pjese të të dhënave në buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Numri i caktuar i bajteve të të dhënave merret dhe hidhet poshtë. Të dhënat më të vjetra janë hedhur poshtë.

Fshirja e të gjitha mesazheve në buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Numëruesi i të dhënave është rivendosur në zero.

Fshirja e të gjitha mesazheve në buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Njehsori nuk rivendoset.

Heqja e tamponit dhe lirimi i memories:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Shembuj të përdorimit të bufferer-it mund të gjenden në kodin burimor të disa filtrave të transmetimit të mediave. Për shembull, në filtrin MS_L16_ENC, i cili riorganizon bajtet në mostrat nga rendi i rrjetit në rendin pritës: l16.c

Në artikullin vijues, ne do të shqyrtojmë çështjen e vlerësimit të ngarkesës në një tregues dhe mënyrat për të luftuar ngarkesën e tepërt të llogaritjes në një transmetues media.

Burimi: www.habr.com

Shto një koment