Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11

Ang materyal ng artikulo ay kinuha mula sa aking zen channel.

Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11

Mekanismo ng paggalaw ng data

  • Data block dblk_t
  • Mensahe mblk_t
  • Mga function para sa pagtatrabaho sa mga mensahe mblk_t
  • Queue queue_t
  • Mga function para sa pagtatrabaho sa mga queue_t
  • Pagkonekta ng mga filter
  • Signal point ng data processing graph
  • Behind the scenes activities ng ticker
  • Bufferizer (MSBufferizer)
  • Mga function para sa pagtatrabaho sa MSBufferizer

Sa huli Artikulo nakagawa kami ng sarili naming filter. Ang artikulong ito ay tumutuon sa panloob na mekanismo para sa paglipat ng data sa pagitan ng mga filter ng media streamer. Papayagan ka nitong magsulat ng mga sopistikadong filter na may kaunting pagsisikap sa hinaharap.

Mekanismo ng paggalaw ng data

Ang paggalaw ng data sa media streamer ay ginagawa gamit ang mga pila na inilarawan ng istraktura pila_t. Mga string ng mga mensahe tulad ng mblk_t, na hindi naglalaman ng data ng signal, ngunit nagli-link lamang sa nakaraan, susunod na mensahe at sa bloke ng data. Bilang karagdagan, gusto kong lalo na bigyang-diin na mayroon ding isang patlang para sa isang link sa isang mensahe ng parehong uri, na nagbibigay-daan sa iyo upang ayusin ang isang solong naka-link na listahan ng mga mensahe. Tatawagin namin ang isang pangkat ng mga mensahe na pinag-isa ng naturang listahan bilang isang tuple. Kaya, ang anumang elemento ng pila ay maaaring maging isang mensahe mblk_t, at marahil ang ulo ng isang tuple ng mensahe mblk_t. Ang bawat tuple message ay maaaring magkaroon ng sarili nitong ward data block. Tatalakayin natin kung bakit kailangan ang mga tuple mamaya.

Tulad ng nabanggit sa itaas, ang mensahe mismo ay hindi naglalaman ng isang bloke ng data; sa halip, naglalaman lamang ito ng isang pointer sa lugar ng memorya kung saan naka-imbak ang bloke. Sa bahaging ito, ang pangkalahatang larawan ng gawain ng media streamer ay nakapagpapaalaala sa bodega ng pinto sa cartoon na "Monsters, Inc.," kung saan ang mga pinto (mga link sa data - mga kwarto) ay gumagalaw sa napakabilis na bilis sa mga overhead conveyor, habang ang mga silid mismo manatiling hindi gumagalaw.

Ngayon, sa paglipat sa hierarchy mula sa ibaba hanggang sa itaas, isaalang-alang natin nang detalyado ang mga nakalistang entity ng mekanismo ng paghahatid ng data sa media streamer.

Block ng data dblk_t

Ang data block ay binubuo ng isang header at isang data buffer. Ang header ay inilalarawan ng sumusunod na istraktura,

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

Ang mga field ng structure ay naglalaman ng mga pointer sa simula ng buffer, sa dulo ng buffer, at ang function para sa pagtanggal ng data buffer. Huling elemento sa header db_ref — reference counter, kung umabot ito sa zero, ito ay nagsisilbing senyales para tanggalin ang block na ito sa memorya. Kung ang data block ay ginawa ng function datab_alloc() , pagkatapos ay ang data buffer ay ilalagay sa memorya kaagad pagkatapos ng header. Sa lahat ng iba pang mga kaso, ang buffer ay maaaring matatagpuan sa isang lugar nang hiwalay. Maglalaman ang buffer ng data ng mga sample ng signal o iba pang data na gusto naming iproseso gamit ang mga filter.

Ang isang bagong instance ng isang data block ay nilikha gamit ang function:

dblk_t *datab_alloc(int size);

Bilang input parameter, binibigyan ito ng laki ng data na iimbak ng block. Higit pang memory ang inilalaan upang maglagay ng header - istraktura - sa simula ng inilaan na memorya datab. Ngunit kapag gumagamit ng iba pang mga function, hindi ito palaging nangyayari; sa ilang mga kaso, ang buffer ng data ay maaaring matatagpuan nang hiwalay mula sa header ng block ng data. Kapag lumilikha ng isang istraktura, ang mga patlang ay na-configure upang ang patlang nito db_base itinuro ang simula ng lugar ng data, at db_lim sa dulo nito. Bilang ng link db_ref ay nakatakda sa isa. Ang data clear function pointer ay nakatakda sa zero.

Mensahe mblk_t

Tulad ng sinabi, ang mga elemento ng pila ay may uri mblk_t, ito ay tinukoy bilang mga sumusunod:

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;

Kaayusan mblk_t naglalaman ng mga payo sa simula b_prev, b_susunod, na kinakailangan upang ayusin ang isang dobleng naka-link na listahan (na isang queue pila_t).

Pagkatapos ay dumating ang pointer b_cont, na ginagamit lamang kapag ang mensahe ay bahagi ng isang tuple. Para sa huling mensahe sa tuple, nananatiling null ang pointer na ito.

Susunod na nakikita namin ang isang pointer sa isang bloke ng data b_datap, kung saan umiiral ang mensahe. Sinusundan ito ng mga pointer sa lugar sa loob ng block data buffer. Patlang b_rptr tumutukoy sa lokasyon kung saan babasahin ang data mula sa buffer. Patlang b_wptr ay nagpapahiwatig ng lokasyon kung saan ang pagsusulat sa buffer ay isasagawa.

Ang natitirang mga patlang ay may likas na serbisyo at hindi nauugnay sa pagpapatakbo ng mekanismo ng paglilipat ng data.

Nasa ibaba ang isang mensahe na may pangalan m1 at data block d1.
Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11
Ang sumusunod na figure ay nagpapakita ng isang tuple ng tatlong mga mensahe m1, m1_1, m1_2.
Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11

Mga function ng pagmemensahe mblk_t

Isang bagong mensahe mblk_t nilikha ng function:

mblk_t *allocb(int size, int pri); 

naglalagay siya ng bagong mensahe sa memorya mblk_t na may data block ng tinukoy na laki laki, pangalawang argumento - pri hindi ginagamit sa bersyong ito ng library. Dapat itong manatiling zero. Sa panahon ng pagpapatakbo ng function, ang memorya ay ilalaan para sa istraktura ng bagong mensahe at ang function ay tatawagin mblk_init(), na magre-reset sa lahat ng field ng nilikhang instance ng structure at pagkatapos, gamit ang nabanggit sa itaas datab_alloc(), ay lilikha ng buffer ng data. Pagkatapos kung saan ang mga patlang sa istraktura ay mai-configure:

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

Sa output nakatanggap kami ng bagong mensahe na may mga nasimulan na field at walang laman na buffer ng data. Upang magdagdag ng data sa isang mensahe, kailangan mong kopyahin ito sa buffer ng block ng data:

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

saan data ay isang pointer sa data source, at laki - ang laki nila.
pagkatapos ay kailangan mong i-update ang pointer sa write point upang ito ay tumuturo muli sa simula ng libreng lugar sa buffer:

msg->b_wptr = msg->b_wptr + size

Kung kailangan mong lumikha ng isang mensahe mula sa isang umiiral na buffer, nang walang pagkopya, pagkatapos ay gamitin ang function na:

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

Ang function, pagkatapos gumawa ng mensahe at ang istraktura ng data block, ay i-configure ang mga pointer nito sa data sa address buf. Yung. sa kasong ito, ang data buffer ay hindi matatagpuan pagkatapos ng mga field ng header ng data block, tulad ng nangyari noong lumilikha ng data block na may function. datab_alloc(). Ang buffer na may data na ipinasa sa function ay mananatili kung saan ito ay, ngunit sa tulong ng mga pointer ito ay naka-attach sa bagong nilikha na header ng data block, at iyon, nang naaayon, sa mensahe.

Sa isang mensahe mblk_t Ang ilang mga bloke ng data ay maaaring pagsamahin nang sunud-sunod. Ginagawa ito ng function:

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

mp — isang mensahe kung saan idadagdag ang isa pang bloke ng data;
data — pointer sa block, isang kopya nito ay idadagdag sa mensahe;
laki - laki ng data;
pad — isang bandila na ang laki ng inilalaang memorya ay dapat na nakahanay sa isang 4-byte na hangganan (ang padding ay gagawin na may mga zero).

Kung may sapat na espasyo sa kasalukuyang buffer ng data ng mensahe, ipe-paste ang bagong data sa likod ng data na naroon na. Kung may mas kaunting libreng espasyo sa buffer ng data ng mensahe kaysa sa laki, pagkatapos ay isang bagong mensahe ang nilikha na may sapat na laki ng buffer at ang data ay kinopya sa buffer nito. Ito ay isang bagong mensahe, na naka-link sa orihinal gamit ang isang pointer b_cont. Sa kasong ito, ang mensahe ay nagiging isang tuple.

Kung kailangan mong magdagdag ng isa pang bloke ng data sa tuple, kailangan mong gamitin ang function:

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

mahahanap niya ang huling mensahe sa tuple (mayroon siya b_cont magiging null) at tatawagin ang function para sa mensaheng ito appendb().

Maaari mong malaman ang laki ng data sa isang mensahe o tuple gamit ang function:

int msgdsize(const mblk_t *mp);

lilipat ito sa lahat ng mga mensahe sa tuple at ibabalik ang kabuuang dami ng data sa mga buffer ng data ng mga mensaheng iyon. Para sa bawat mensahe, ang dami ng data ay kinakalkula tulad ng sumusunod:

 mp->b_wptr - mp->b_rptr

Upang pagsamahin ang dalawang tuple, gamitin ang function:

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

idinagdag niya ang tuple newm sa buntot ng tuple mp at nagbabalik ng pointer sa huling mensahe ng resultang tuple.

Kung kinakailangan, ang isang tuple ay maaaring gawing isang mensahe na may isang solong bloke ng data; ito ay ginagawa ng function:

void msgpullup(mblk_t *mp,int len);

kung argumento Len ay -1, pagkatapos ay awtomatikong tinutukoy ang laki ng inilalaang buffer. Kung Len ay isang positibong numero, isang buffer na ganito ang laki ay gagawin at ang tuple na data ng mensahe ay makokopya dito. Kung maubusan ang buffer, titigil doon ang pagkopya. Ang unang mensahe ng tuple ay makakatanggap ng bagong laki ng buffer na may nakopyang data. Ang natitirang mga mensahe ay tatanggalin at ang memorya ay ibabalik sa heap.

Kapag nagtatanggal ng isang istraktura mblk_t ang bilang ng sanggunian ng data block ay isinasaalang-alang kung, kapag tumatawag freeb() ito ay lumalabas na zero, pagkatapos ay ang data buffer ay tinanggal kasama ng halimbawa mblk_t, na tumuturo dito.

Pagsisimula sa mga field ng isang bagong mensahe:

void mblk_init(mblk_t *mp);

Pagdaragdag ng isa pang piraso ng data sa mensahe:

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

Kung ang bagong data ay hindi magkasya sa libreng espasyo ng buffer ng data ng mensahe, pagkatapos ay isang hiwalay na nilikhang mensahe na may buffer ng kinakailangang laki ay naka-attach sa mensahe (isang pointer sa idinagdag na mensahe ay nakatakda sa unang mensahe) at ang nagiging tuple ang mensahe.

Pagdaragdag ng isang piraso ng data sa isang tuple:

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

Tinatawag ng function ang appendb() sa isang loop.

Pagsasama-sama ng dalawang tuple sa isa:

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

Mensahe newm ay ikakabit sa mp.

Paggawa ng kopya ng iisang mensahe:

mblk_t *copyb(const mblk_t *mp);

Kumpletuhin ang pagkopya ng isang tuple kasama ang lahat ng mga bloke ng data:

mblk_t *copymsg(const mblk_t *mp);

Ang mga elemento ng tuple ay kinopya ng function copyb().

Gumawa ng madaling kopya ng isang mensahe mblk_t. Sa kasong ito, ang data block ay hindi kinopya, ngunit ang reference counter nito ay tumaas db_ref:

mblk_t *dupb(mblk_t *mp);

Paggawa ng magaan na kopya ng isang tuple. Ang mga bloke ng data ay hindi kinokopya, tanging ang kanilang mga reference counter ay dinadagdagan db_ref:

mblk_t *dupmsg(mblk_t* m);

Pinagdikit ang lahat ng mensahe ng isang tuple sa isang mensahe:

void msgpullup(mblk_t *mp,size_t len);

Kung ang argumento Len ay -1, pagkatapos ay awtomatikong tinutukoy ang laki ng inilalaang buffer.

Pagtanggal ng mensahe, tuple:

void freemsg(mblk_t *mp);

Binabawasan ng isa ang bilang ng sanggunian ng data block. Kung umabot ito sa zero, tatanggalin din ang data block.

Pagkalkula ng kabuuang dami ng data sa isang mensahe o tuple.

size_t msgdsize(const mblk_t *mp);

Pagkuha ng mensahe mula sa buntot ng pila:

mblk_t *ms_queue_peek_last (q);

Pagkopya sa mga nilalaman ng mga nakalaan na field ng isang mensahe patungo sa isa pang mensahe (sa katunayan, ang mga field na ito ay naglalaman ng mga flag na ginagamit ng media streamer):

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

Queue pila_t

Ang pila ng mensahe sa streamer ng media ay ipinatupad bilang isang pabilog na dobleng naka-link na listahan. Ang bawat elemento ng listahan ay naglalaman ng isang pointer sa isang bloke ng data na may mga sample ng signal. Lumalabas na ang mga pointer lamang sa data block ang gumagalaw, habang ang data mismo ay nananatiling hindi gumagalaw. Yung. tanging mga link sa kanila ang inililipat.
Istraktura na naglalarawan sa pila pila_t, ipinapakita sa ibaba:

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

Ang istraktura ay naglalaman ng isang patlang - isang pointer _q_stopper type *mblk_t, itinuturo nito ang unang elemento (mensahe) sa pila. Ang pangalawang field ng structure ay ang counter ng mga mensahe sa queue.
Ang figure sa ibaba ay nagpapakita ng isang queue na pinangalanang q1 na naglalaman ng 4 na mensahe m1, m2, m3, m4.
Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11
Ang sumusunod na figure ay nagpapakita ng isang queue na pinangalanang q1 na naglalaman ng 4 na mensahe m1,m2,m3,m4. Ang mensaheng m2 ay ang ulo ng isang tuple na naglalaman ng dalawa pang mensahe na m2_1 at m2_2.

Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 11

Mga function para sa pagtatrabaho sa mga queue_t

Pagsisimula ng queue:

void qinit(queue_t *q);

Field _q_stopper (simula dito ay tatawagin natin itong "stopper") ay pinasimulan ng function mblk_init(), ang dating elemento nito at ang susunod na pointer ng elemento ay inaayos upang tumuro sa sarili nito. Ang queue element counter ay ni-reset sa zero.

Pagdaragdag ng bagong elemento (mga mensahe):

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

Bagong elemento m ay idinagdag sa dulo ng listahan, ang mga pointer ng elemento ay inaayos upang ang stopper ay maging susunod na elemento para dito, at ito ay naging nakaraang elemento para sa stopper. Ang counter ng elemento ng pila ay nadagdagan.

Pagkuha ng elemento mula sa queue:

mblk_t * getq(queue_t *q); 

Ang mensahe na darating pagkatapos ng stopper ay nakuha, at ang element counter ay nababawasan. Kung walang mga elemento sa pila maliban sa stopper, ibinabalik ang 0.

Paglalagay ng mensahe sa isang pila:

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

Element mp ipinasok bago ang elemento emp. Kung emp=0, pagkatapos ay idaragdag ang mensahe sa buntot ng pila.

Pagkuha ng mensahe mula sa ulo ng pila:

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

Ang counter ng elemento ay nabawasan.

Pagbabasa ng pointer sa unang elemento sa queue:

mblk_t * peekq(queue_t *q); 

Pag-alis ng lahat ng elemento mula sa pila habang tinatanggal ang mga elemento mismo:

void flushq(queue_t *q, int how);

argumento paano hindi ginagamit. Ang queue element counter ay nakatakda sa zero.

Macro para sa pagbabasa ng isang pointer sa huling elemento ng queue:

mblk_t * qlast(queue_t *q);

Kapag nagtatrabaho sa mga pila ng mensahe, tandaan na kapag tumawag ka ms_queue_put(q, m) na may null pointer sa mensahe, ang function na mga loop. Mag-freeze ang iyong programa. pareho ang ugali ms_queue_next(q, m).

Pagkonekta ng mga filter

Ang pila na inilarawan sa itaas ay ginagamit upang magpasa ng mga mensahe mula sa isang filter patungo sa isa pa o mula sa isa hanggang sa ilang mga filter. Ang mga filter at ang kanilang mga koneksyon ay bumubuo ng isang nakadirekta na graph. Ang input o output ng filter ay tatawaging pangkalahatang salitang "pin". Upang ilarawan ang pagkakasunud-sunod kung saan ang mga filter ay konektado sa isa't isa, ginagamit ng media streamer ang konsepto ng isang "signal point." Ang punto ng signal ay istraktura _MSCPoint, na naglalaman ng isang pointer sa filter at ang bilang ng isa sa mga pin nito; ayon dito, inilalarawan nito ang koneksyon ng isa sa mga input o output ng filter.

Signal point ng data processing graph

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

Ang mga pin ng filter ay binibilang simula sa zero.

Ang koneksyon ng dalawang pin sa pamamagitan ng isang queue ng mensahe ay inilarawan ng istraktura _MSQueue, na naglalaman ng queue ng mensahe at mga pointer sa dalawang signal point na ikinokonekta nito:

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

Tatawagin namin ang istrukturang ito bilang isang link ng signal. Ang bawat media streamer filter ay naglalaman ng isang talahanayan ng mga input link at isang talahanayan ng mga output link (MQueue). Ang laki ng mga talahanayan ay nakatakda kapag gumagawa ng isang filter; nagawa na namin ito gamit ang isang na-export na variable ng uri MSFilterDesc, nang bumuo kami ng sarili naming filter. Nasa ibaba ang isang istraktura na naglalarawan ng anumang filter sa isang media streamer, 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;

Pagkatapos naming ikonekta ang mga filter sa programang C alinsunod sa aming plano (ngunit hindi ikinonekta ang ticker), sa gayon ay lumikha kami ng isang nakadirekta na graph, ang mga node kung saan ay mga pagkakataon ng istraktura MSFilter, at ang mga gilid ay mga pagkakataon ng mga link MQueue.

Behind the scenes activities ng ticker

Nang sabihin ko sa iyo na ang ticker ay isang filter para sa pinagmulan ng mga ticks, hindi ito ang buong katotohanan tungkol dito. Ang ticker ay isang bagay na nagpapatakbo ng mga function sa orasan proseso() lahat ng mga filter ng circuit (graph) kung saan ito konektado. Kapag ikinonekta namin ang isang ticker sa isang graph filter sa isang C program, ipinapakita namin sa ticker ang graph na kokontrolin nito mula ngayon hanggang sa i-off namin ito. Pagkatapos kumonekta, magsisimulang suriin ng ticker ang graph na ipinagkatiwala sa pangangalaga nito, na nag-compile ng listahan ng mga filter na kinabibilangan nito. Upang hindi "bilangin" ang parehong filter nang dalawang beses, minarkahan nito ang mga nakitang filter sa pamamagitan ng paglalagay ng checkbox sa mga ito nakikita. Isinasagawa ang paghahanap gamit ang mga talahanayan ng link na mayroon ang bawat filter.

Sa panahon ng panimulang paglilibot nito sa graph, sinusuri ng ticker kung sa mga filter ay mayroong kahit isa na nagsisilbing mapagkukunan ng mga bloke ng data. Kung wala, ituturing na mali ang graph at nag-crash ang ticker.

Kung ang graph ay lumabas na "tama", para sa bawat nahanap na filter, ang function ay tinatawag para sa pagsisimula preprocess(). Sa sandaling dumating ang sandali para sa susunod na ikot ng pagproseso (bawat 10 millisecond bilang default), tatawagin ng ticker ang function proseso() para sa lahat ng dating nahanap na source na filter, at pagkatapos ay para sa natitirang mga filter sa listahan. Kung ang filter ay may mga input link, pagkatapos ay patakbuhin ang function proseso() umuulit hanggang ang mga queue ng input link ay walang laman. Pagkatapos nito, lilipat ito sa susunod na filter sa listahan at "i-scroll" ito hanggang sa mawalan ng mga mensahe ang mga input link. Lumilipat ang ticker mula sa filter patungo sa filter hanggang sa matapos ang listahan. Kinukumpleto nito ang pagproseso ng cycle.

Ngayon ay babalik tayo sa tuples at pag-uusapan kung bakit idinagdag ang naturang entity sa streamer ng media. Sa pangkalahatan, ang dami ng data na kinakailangan ng algorithm na tumatakbo sa loob ng filter ay hindi nagtutugma at hindi isang multiple ng laki ng mga buffer ng data na natanggap sa input. Halimbawa, sumusulat kami ng isang filter na nagsasagawa ng mabilis na pagbabagong Fourier, na sa pamamagitan ng kahulugan ay maaari lamang magproseso ng mga bloke ng data na ang laki ay isang kapangyarihan ng dalawa. Hayaan itong maging 512 bilang. Kung ang data ay nabuo ng isang channel ng telepono, ang data buffer ng bawat mensahe sa input ay magdadala sa amin ng 160 signal sample. Nakatutukso na huwag mangolekta ng data mula sa input hangga't hindi naroroon ang kinakailangang dami ng data. Ngunit sa kasong ito, magkakaroon ng banggaan sa ticker, na hindi matagumpay na susubukang i-scroll ang filter hanggang sa walang laman ang input link. Noong nakaraan, itinalaga namin ang panuntunang ito bilang ikatlong prinsipyo ng filter. Ayon sa prinsipyong ito, dapat kunin ng process() function ng filter ang lahat ng data mula sa mga input queues.

Bilang karagdagan, hindi posible na kumuha lamang ng 512 na mga sample mula sa input, dahil maaari ka lamang kumuha ng buong mga bloke, i.e. ang filter ay kailangang kumuha ng 640 sample at gumamit ng 512 sa mga ito, ang natitira bago makaipon ng bagong bahagi ng data. Kaya, ang aming filter, bilang karagdagan sa pangunahing gawain nito, ay dapat magbigay ng mga pantulong na aksyon para sa intermediate na pag-iimbak ng data ng pag-input. Ang mga nag-develop ng media streamer at solusyon sa pangkalahatang problemang ito ay nakabuo ng isang espesyal na bagay - MSBufferizer (bufferer), na nalulutas ang problemang ito gamit ang mga tuple.

Bufferizer (MSBufferizer)

Ito ay isang bagay na mag-iipon ng data ng input sa loob ng filter at magsisimulang isumite ito para sa pagproseso sa sandaling sapat na ang dami ng impormasyon upang patakbuhin ang algorithm ng filter. Habang ang buffer ay nag-iipon ng data, ang filter ay gagana sa idle mode, nang hindi ginagamit ang kapangyarihan sa pagpoproseso ng processor. Ngunit sa sandaling ang function ng pagbabasa mula sa bufferer ay nagbabalik ng isang halaga maliban sa zero, ang process() function ng filter ay magsisimulang kumuha at magproseso ng data mula sa bufferer sa mga bahagi ng kinakailangang laki, hanggang sa ito ay maubos.
Ang data na hindi pa kinakailangan ay nananatili sa buffer bilang unang elemento ng tuple, kung saan ang mga kasunod na bloke ng input data ay nakalakip.

Ang istraktura na naglalarawan sa buffer:

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

Mga function para sa pagtatrabaho sa MSBufferizer

Paglikha ng bagong buffer instance:

MSBufferizer * ms_bufferizer_new(void);

Inilalaan ang memorya, pinasimulan sa ms_bufferizer_init() at ibinalik ang isang pointer.

Pag-andar ng pagsisimula:

void ms_bufferizer_init(MSBufferizer *obj); 

Nagsisimula na ang pila q, patlang laki ay nakatakda sa zero.

Pagdaragdag ng mensahe:

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

Ang mensahe m ay idinagdag sa pila. Ang kinakalkula na laki ng mga bloke ng data ay idinagdag sa laki.

Paglilipat ng lahat ng mensahe mula sa queue ng data ng link patungo sa buffer q:

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

Paglilipat ng mga mensahe mula sa isang link q sa buffer ay ginanap gamit ang function ms_bufferizer_put().

Pagbasa mula sa buffer:

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

Kung ang laki ng data na naipon sa buffer ay mas mababa kaysa sa hiniling (datalen), pagkatapos ang function ay nagbabalik ng zero, ang data ay hindi kinopya sa data. Kung hindi, ang sunud-sunod na pagkopya ng data mula sa mga tuple na matatagpuan sa buffer ay isinasagawa. Pagkatapos ng pagkopya, ang tuple ay tinanggal at ang memorya ay napalaya. Nagtatapos ang pagkopya sa sandaling makopya ang mga datalen byte. Kung maubusan ang espasyo sa gitna ng isang data block, sa mensaheng ito, ang data block ay mababawasan sa natitirang bahaging hindi nakopya. Sa susunod na tumawag ka, magpapatuloy ang pagkopya mula sa puntong ito.

Binabasa ang dami ng data na kasalukuyang available sa buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Ibinabalik ang field laki bufferer.

Pagtatapon ng bahagi ng data sa buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Ang tinukoy na bilang ng mga byte ng data ay kinukuha at itinatapon. Ang pinakalumang data ay itinapon.

Tinatanggal ang lahat ng mensahe sa buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Ang data counter ay ni-reset sa zero.

Tinatanggal ang lahat ng mensahe sa buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Hindi na-reset ang counter.

Pag-alis ng buffer at pagpapalaya ng memorya:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Ang mga halimbawa ng paggamit ng bufferer ay makikita sa source code ng ilang media streamer filter. Halimbawa, sa filter na MS_L16_ENC, na muling nagsasaayos ng mga byte sa mga sample mula sa pagkakasunud-sunod ng network patungo sa pagkakasunud-sunod ng host: l16.c

Sa susunod na artikulo, titingnan natin ang isyu ng pagtatantya ng load sa isang ticker at mga paraan upang labanan ang labis na pagkarga ng computing sa isang media streamer.

Pinagmulan: www.habr.com

Magdagdag ng komento