Mediastreamer2 VoIP programmas izpēte. 11. daļa

Raksta materiāls ņemts no mana zen kanāls.

Mediastreamer2 VoIP programmas izpēte. 11. daļa

Datu kustības mehānisms

  • Datu bloks dblk_t
  • Ziņojums mblk_t
  • Funkcijas darbam ar ziņojumiem mblk_t
  • Rinda queue_t
  • Funkcijas darbam ar rindām queue_t
  • Filtru pievienošana
  • Datu apstrādes grafika signāla punkts
  • Biržas darbības aizkulisēs
  • Buferizators (MSBufferizer)
  • Funkcijas darbam ar MSBufferizer

Pagātnē raksts esam izstrādājuši paši savu filtru. Šajā rakstā galvenā uzmanība tiks pievērsta iekšējam mehānismam datu pārvietošanai starp multivides straumēšanas filtriem. Tas ļaus turpmāk rakstīt sarežģītus filtrus ar mazāku piepūli.

Datu kustības mehānisms

Datu pārvietošana multivides straumētājā tiek veikta, izmantojot struktūras aprakstītās rindas rinda_t. Tādu ziņojumu virknes kā mblk_t, kas paši nesatur signāla datus, bet tikai saites uz iepriekšējo, nākamo ziņojumu un uz datu bloku. Turklāt es vēlos īpaši uzsvērt, ka ir arī lauks saitei uz tāda paša veida ziņojumu, kas ļauj sakārtot atsevišķi saistītu ziņojumu sarakstu. Ziņojumu grupu, ko apvieno šāds saraksts, mēs sauksim par korteci. Tādējādi jebkurš rindas elements var būt viens ziņojums mblk_t, un varbūt ziņu virknes galva mblk_t. Katram kortedža ziņojumam var būt savs palātas datu bloks. Kāpēc korteži ir nepieciešami, mēs apspriedīsim nedaudz vēlāk.

Kā minēts iepriekš, pašā ziņojumā nav datu bloka; tā vietā tajā ir tikai norāde uz atmiņas apgabalu, kurā bloks ir saglabāts. Šajā daļā mediju straumētāja darba kopaina atgādina multfilmas “Monsters, Inc.” durvju noliktavu, kur durvis (saites uz datiem - telpas) pārvietojas neprātīgā ātrumā pa gaisvadu konveijeriem, savukārt pašas telpas palikt nekustīgam.

Tagad, virzoties pa hierarhiju no apakšas uz augšu, detalizēti apsvērsim multivides straumētājā uzskaitītās datu pārraides mehānisma entītijas.

Datu bloks dblk_t

Datu bloks sastāv no galvenes un datu bufera. Galveni apraksta šāda struktūra,

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

Struktūras laukos ir norādes uz bufera sākumu, bufera beigām un datu bufera dzēšanas funkciju. Pēdējais elements galvenē db_ref — atsauces skaitītājs, ja tas sasniedz nulli, tas kalpo kā signāls šī bloka dzēšanai no atmiņas. Ja datu bloku izveidoja funkcija datab_alloc() , tad datu buferis tiks ievietots atmiņā uzreiz aiz galvenes. Visos citos gadījumos buferis var atrasties kaut kur atsevišķi. Datu buferī būs signālu paraugi vai citi dati, kurus vēlamies apstrādāt ar filtriem.

Jauns datu bloka gadījums tiek izveidots, izmantojot funkciju:

dblk_t *datab_alloc(int size);

Kā ievades parametrs tiek norādīts datu lielums, ko bloks saglabās. Vairāk atmiņas tiek atvēlēts, lai piešķirtās atmiņas sākumā novietotu galveni - struktūru datub. Bet, izmantojot citas funkcijas, tas ne vienmēr notiek; dažos gadījumos datu buferis var atrasties atsevišķi no datu bloka galvenes. Veidojot struktūru, lauki tiek konfigurēti tā, lai tā lauks db_base norādīja uz datu apgabala sākumu un db_lim līdz tās beigām. Saišu skaits db_ref ir iestatīts uz vienu. Datu dzēšanas funkcijas rādītājs ir iestatīts uz nulli.

Ziņa mblk_t

Kā minēts, rindas elementi ir tipa mblk_t, tas ir definēts šādi:

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;

Struktūra mblk_t sākumā ir norādes b_iepriekš, b_next, kas ir nepieciešami, lai sakārtotu divkārši saistītu sarakstu (kas ir rinda rinda_t).

Tad nāk rādītājs b_cont, ko izmanto tikai tad, ja ziņojums ir daļa no kortedža. Pēdējam ziņojumam korejā šis rādītājs paliek nulle.

Tālāk mēs redzam rādītāju uz datu bloku b_datap, par kuru pastāv ziņojums. Tam seko norādes uz apgabalu bloka datu buferī. Lauks b_rptr norāda vietu, no kuras tiks nolasīti dati no bufera. Lauks b_wptr norāda vietu, no kuras tiks veikta rakstīšana buferī.

Pārējie lauki ir pakalpojumu raksturs un nav saistīti ar datu pārsūtīšanas mehānisma darbību.

Zemāk ir viens ziņojums ar nosaukumu m1 un datu bloks d1.
Mediastreamer2 VoIP programmas izpēte. 11. daļa
Nākamajā attēlā parādīts trīs ziņojumu kopums m1, m1_1, m1_2.
Mediastreamer2 VoIP programmas izpēte. 11. daļa

Ziņapmaiņas funkcijas mblk_t

Jauna ziņa mblk_t izveidots ar funkciju:

mblk_t *allocb(int size, int pri); 

viņa ievieto atmiņā jaunu ziņojumu mblk_t ar norādītā izmēra datu bloku izmērs, otrais arguments - pri netiek izmantots šajā bibliotēkas versijā. Tam vajadzētu palikt nullei. Funkcijas darbības laikā tiks atvēlēta atmiņa jaunā ziņojuma struktūrai un funkcija tiks izsaukta mblk_init(), kas atiestatīs visus izveidotās struktūras instances laukus un pēc tam, izmantojot iepriekš minēto datab_alloc(), izveidos datu buferi. Pēc tam struktūras lauki tiks konfigurēti:

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

Izvadā mēs saņemam jaunu ziņojumu ar inicializētiem laukiem un tukšu datu buferi. Lai ziņojumam pievienotu datus, tie jākopē datu bloka buferī:

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

kur dati ir rādītājs uz datu avotu un izmērs - to izmērs.
tad jums ir jāatjaunina rādītājs uz rakstīšanas punktu, lai tas atkal norādītu uz bufera brīvās apgabala sākumu:

msg->b_wptr = msg->b_wptr + size

Ja jums ir jāizveido ziņojums no esoša bufera, nekopējot, izmantojiet funkciju:

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

Funkcija pēc ziņojuma un datu bloka struktūras izveidošanas konfigurēs savus rādītājus uz datiem adresē mīlulis. Tie. šajā gadījumā datu buferis neatrodas aiz datu bloka galvenes laukiem, kā tas bija veidojot datu bloku ar funkciju datab_alloc(). Buferis ar funkcijai nodotajiem datiem paliks tur, kur bija, bet ar rādītāju palīdzību tiks pievienots jaunizveidotajai datu bloka galvenei un attiecīgi arī ziņojumam.

Uz vienu ziņu mblk_t Vairākus datu blokus var secīgi savienot. To veic funkcija:

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

mp — ziņojums, kuram tiks pievienots cits datu bloks;
dati — rādītājs uz bloku, kura kopija tiks pievienota ziņojumam;
izmērs — datu apjoms;
paliktnis — karodziņš, ka piešķirtās atmiņas lielums ir jāsaskaņo pa 4 baitu robežu (pildījums tiks veikts ar nullēm).

Ja esošajā ziņojumu datu buferī ir pietiekami daudz vietas, jaunie dati tiks ielīmēti aiz jau esošajiem datiem. Ja ziņojuma datu buferī ir mazāk brīvas vietas nekā izmērs, tad tiek izveidots jauns ziņojums ar pietiekamu bufera lielumu un dati tiek kopēti tā buferī. Šis ir jauns ziņojums, kas, izmantojot rādītāju, ir saistīts ar sākotnējo b_cont. Šajā gadījumā ziņojums pārvēršas kortežā.

Ja kortei jāpievieno vēl viens datu bloks, tad jāizmanto funkcija:

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

viņa atradīs pēdējo ziņojumu virknē (viņam ir b_cont būs nulle) un izsauks šī ziņojuma funkciju apendb().

Varat uzzināt ziņojumā vai virknē esošo datu lielumu, izmantojot funkciju:

int msgdsize(const mblk_t *mp);

tas cilpas cauri visiem korejā esošajiem ziņojumiem un atgriezīs kopējo datu apjomu šo ziņojumu datu buferos. Katram ziņojumam datu apjoms tiek aprēķināts šādi:

 mp->b_wptr - mp->b_rptr

Lai apvienotu divas kortežas, izmantojiet funkciju:

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

viņa pievieno virkni jaunums līdz kortedža astei mp un atgriež rādītāju uz iegūtās korejas pēdējo ziņojumu.

Ja nepieciešams, kortežāru var pārvērst par vienu ziņojumu ar vienu datu bloku; to dara funkcija:

void msgpullup(mblk_t *mp,int len);

ja arguments LEN ir -1, tad piešķirtā bufera lielums tiek noteikts automātiski. Ja LEN ir pozitīvs skaitlis, tiks izveidots šāda izmēra buferis un tajā tiks iekopēti kortedža ziņojuma dati. Ja buferis beidzas, kopēšana tiks pārtraukta. Korejas pirmais ziņojums saņems jauna izmēra buferi ar kopētajiem datiem. Atlikušie ziņojumi tiks dzēsti, un atmiņa tiks atgriezta kaudzē.

Dzēšot struktūru mblk_t datu bloka atsauces skaits tiek ņemts vērā, ja, zvanot freeb() izrādās nulle, tad datu buferis tiek dzēsts kopā ar gadījumu mblk_t, kas uz to norāda.

Jauna ziņojuma lauku inicializācija:

void mblk_init(mblk_t *mp);

Vēl viena datu pievienošana ziņojumam:

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

Ja jaunie dati neietilpst ziņojuma datu bufera brīvajā vietā, ziņojumam tiek pievienots atsevišķi izveidots ziņojums ar vajadzīgā lieluma buferi (pirmajā ziņojumā tiek iestatīts rādītājs uz pievienoto ziņojumu) un ziņa pārvēršas par virkni.

Datu daļas pievienošana kortei:

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

Funkcija appendb() izsauc cilpā.

Divu kopu apvienošana vienā:

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

Ziņa jaunums tiks pievienots mp.

Viena ziņojuma kopijas izveide:

mblk_t *copyb(const mblk_t *mp);

Korejas pilnīga kopēšana ar visiem datu blokiem:

mblk_t *copymsg(const mblk_t *mp);

Korejas elementus kopē funkcija copyb ().

Izveidojiet vienkāršu ziņojuma kopiju mblk_t. Šajā gadījumā datu bloks netiek kopēts, bet tiek palielināts tā atsauces skaitītājs db_ref:

mblk_t *dupb(mblk_t *mp);

Korpusa vieglas kopijas izveidošana. Datu bloki netiek kopēti, tiek palielināti tikai to atsauces skaitītāji db_ref:

mblk_t *dupmsg(mblk_t* m);

Visu korektora ziņojumu salīmēšana vienā ziņojumā:

void msgpullup(mblk_t *mp,size_t len);

Ja arguments LEN ir -1, tad piešķirtā bufera lielums tiek noteikts automātiski.

Ziņas dzēšana, korte:

void freemsg(mblk_t *mp);

Datu bloka atsauces skaits tiek samazināts par vienu. Ja tas sasniedz nulli, tiek dzēsts arī datu bloks.

Kopējā datu apjoma aprēķināšana ziņojumā vai kortežā.

size_t msgdsize(const mblk_t *mp);

Ziņojuma izgūšana no rindas gala:

mblk_t *ms_queue_peek_last (q);

Viena ziņojuma rezervēto lauku satura kopēšana citā ziņojumā (faktiski šajos laukos ir karodziņi, kurus izmanto multivides straumētājs):

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

Rinda rinda_t

Ziņojumu rinda multivides straumētājā ir ieviesta kā apļveida, divkārši saistīts saraksts. Katrs saraksta elements satur rādītāju uz datu bloku ar signālu paraugiem. Izrādās, ka pēc kārtas pārvietojas tikai norādes uz datu bloku, savukārt paši dati paliek nekustīgi. Tie. tiek pārvietotas tikai saites uz tām.
Struktūra, kas apraksta rindu rinda_t, parādīts zemāk:

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

Struktūra satur lauku - rādītāju _q_aizbāznis ierakstiet *mblk_t, tas norāda uz pirmo elementu (ziņojumu) rindā. Otrais struktūras lauks ir rindā esošo ziņojumu skaitītājs.
Zemāk esošajā attēlā ir parādīta rinda ar nosaukumu q1, kurā ir 4 ziņojumi m1, m2, m3, m4.
Mediastreamer2 VoIP programmas izpēte. 11. daļa
Nākamajā attēlā parādīta rinda ar nosaukumu q1, kurā ir 4 ziņojumi m1,m2,m3,m4. Ziņojums m2 ir kortedža galva, kurā ir vēl divi ziņojumi m2_1 un m2_2.

Mediastreamer2 VoIP programmas izpēte. 11. daļa

Funkcijas darbam ar rindām queue_t

Rindas inicializācija:

void qinit(queue_t *q);

Lauks _q_aizbāznis (turpmāk mēs to sauksim par "aizbāzni") tiek inicializēta ar funkciju mblk_init(), tā iepriekšējais elements un nākamā elementa rādītājs tiek pielāgoti, lai norādītu uz sevi. Rindas elementu skaitītājs tiek atiestatīts uz nulli.

Jauna elementa pievienošana (ziņojumi):

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

Jauns elements m ir pievienots saraksta beigās, elementu norādes tiek noregulētas tā, lai aizbāznis kļūtu par nākamo elementu tam, un tas kļūst par iepriekšējo elementu aizbāznim. Rindas elementu skaitītājs tiek palielināts.

Elementa izgūšana no rindas:

mblk_t * getq(queue_t *q); 

Ziņojums, kas tiek parādīts pēc aizbāžņa, ir izgūts, un elementu skaitītājs tiek samazināts. Ja rindā nav elementu, izņemot aizbāzni, tiek atgriezts 0.

Ziņojuma ievietošana rindā:

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

Elements mp ievietots pirms elementa EMP. Ja EMP=0, tad ziņojums tiek pievienots rindas galam.

Ziņojuma izgūšana no rindas galvas:

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

Elementu skaitītājs tiek samazināts.

Rādītāja nolasīšana uz pirmo rindā esošo elementu:

mblk_t * peekq(queue_t *q); 

Visu elementu noņemšana no rindas, vienlaikus dzēšot pašus elementus:

void flushq(queue_t *q, int how);

Arguments cik nav izmantots. Rindas elementu skaitītājs ir iestatīts uz nulli.

Makro rādītāja nolasīšanai uz rindas pēdējo elementu:

mblk_t * qlast(queue_t *q);

Strādājot ar ziņojumu rindām, ņemiet vērā, ka zvanot ms_queue_put(q, m) ar nulles rādītāju uz ziņojumu, funkcija cilpas. Jūsu programma iesaldēsies. uzvedas līdzīgi ms_queue_next(q, m).

Filtru pievienošana

Iepriekš aprakstītā rinda tiek izmantota, lai nosūtītu ziņojumus no viena filtra uz otru vai no viena uz vairākiem filtriem. Filtri un to savienojumi veido virzītu grafiku. Filtra ievade vai izvade tiks saukta par vispārīgo vārdu “pin”. Lai aprakstītu secību, kādā filtri ir savienoti viens ar otru, multivides straumētājs izmanto jēdzienu “signāla punkts”. Signāla punkts ir struktūra _MSCPaint, kas satur rādītāju uz filtru un vienas tā tapas numuru; attiecīgi tas apraksta vienas no filtra ieejām vai izejām savienojumu.

Datu apstrādes grafika signāla punkts

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

Filtra tapas ir numurētas, sākot no nulles.

Divu tapu savienojumu ar ziņojumu rindu apraksta struktūra _MSQueue, kurā ir ziņojumu rinda un norādes uz diviem signāla punktiem, ko tas savieno:

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

Mēs šo struktūru sauksim par signāla saiti. Katrs multivides straumētāja filtrs satur ievades saišu tabulu un izvades saišu tabulu (MSQueue). Tabulu lielums tiek iestatīts, veidojot filtru; mēs to jau esam izdarījuši, izmantojot eksportētu tipa mainīgo MSFilterDesc, kad mēs izstrādājām paši savu filtru. Tālāk ir sniegta struktūra, kas apraksta jebkuru multivides straumētāja filtru, MSFfiltrs:


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;

Pēc tam, kad C programmā mēs savienojām filtrus saskaņā ar mūsu plānu (bet nesavienojām sviru), mēs izveidojām virzītu grafiku, kura mezgli ir struktūras gadījumi. MSFfiltrs, un malas ir saišu gadījumi MSQueue.

Biržas darbības aizkulisēs

Kad es jums teicu, ka svārsts ir ērču avota filtrs, tā nebija visa patiesība par to. Svārstītājs ir objekts, kas veic pulksteņa funkcijas process () visi tās ķēdes filtri (grafika), kurai tā ir pievienota. Kad programmā C pievienojam slīdni ar grafika filtru, mēs parādām slīdnim grafiku, kuru tas vadīs no šī brīža līdz izslēgšanai. Pēc savienojuma izveides svārsts sāk pārbaudīt tā aprūpē uzticēto grafiku, sastādot filtru sarakstu, kas to ietver. Lai viens un tas pats filtrs netiktu “skaitīts” divas reizes, tas atzīmē atklātos filtrus, ievietojot tajos izvēles rūtiņu redzējis. Meklēšana tiek veikta, izmantojot saišu tabulas, kas ir katram filtram.

Diagrammas ievadceļojuma laikā svārsts pārbauda, ​​vai starp filtriem ir vismaz viens, kas darbojas kā datu bloku avots. Ja tādu nav, grafiks tiek uzskatīts par nepareizu un svārsts avarē.

Ja grafiks izrādās “pareizs”, katram atrastajam filtram funkcija tiek izsaukta inicializācijai priekšapstrāde (). Tiklīdz pienāk brīdis nākamajam apstrādes ciklam (pēc noklusējuma ik pēc 10 milisekundēm), svārsts izsauc funkciju process () visiem iepriekš atrastajiem avota filtriem un pēc tam atlikušajiem sarakstā esošajiem filtriem. Ja filtram ir ievades saites, palaidiet funkciju process () atkārtojas, līdz ievades saišu rindas ir tukšas. Pēc tam tas pāriet uz nākamo sarakstā esošo filtru un “ritina” to, līdz ievades saitēs vairs nav ziņojumu. Atzīme pārvietojas no viena filtra uz filtru, līdz saraksts beidzas. Tas pabeidz cikla apstrādi.

Tagad mēs atgriezīsimies pie kortežām un runāsim par to, kāpēc šāda entītija tika pievienota multivides straumētājam. Kopumā filtra iekšienē strādājošajam algoritmam nepieciešamais datu apjoms nesakrīt un nav ieejā saņemto datu buferu lieluma reizinājums. Piemēram, mēs rakstām filtru, kas veic ātru Furjē transformāciju, kas pēc definīcijas var apstrādāt tikai datu blokus, kuru izmērs ir divi. Lai tas būtu 512 skaits. Ja datus ģenerē telefona kanāls, tad katra ziņojuma datu buferis ieejā atnesīs mums 160 signālu paraugus. Ir vilinoši neievākt datus no ievades, kamēr nav pieejams nepieciešamais datu apjoms. Bet šajā gadījumā notiks sadursme ar atzīmi, kas neveiksmīgi mēģinās ritināt filtru, līdz ievades saite ir tukša. Iepriekš mēs norādījām šo noteikumu kā trešo filtra principu. Saskaņā ar šo principu filtra process() funkcijai ir jāņem visi dati no ievades rindām.

Turklāt no ievades nevarēs ņemt tikai 512 paraugus, jo jūs varat ņemt tikai veselus blokus, t.i. filtram būs jāņem 640 paraugi un jāizmanto 512 no tiem, atlikušie pirms jaunas datu daļas uzkrāšanas. Tādējādi mūsu filtram papildus galvenajam darbam ir jānodrošina palīgdarbības ievades datu starpposma uzglabāšanai. Multivides straumētāja un šīs vispārīgās problēmas risinājuma izstrādātāji ir izstrādājuši īpašu objektu - MSBufferizer (buferis), kas šo problēmu atrisina, izmantojot korešus.

Buferizators (MSBufferizer)

Šis ir objekts, kas filtrā uzkrās ievades datus un sāks tos iesniegt apstrādei, tiklīdz informācijas apjoms būs pietiekams, lai palaistu filtra algoritmu. Kamēr buferis uzkrāj datus, filtrs darbosies dīkstāves režīmā, neizmantojot procesora apstrādes jaudu. Taču, tiklīdz nolasīšanas funkcija no buferiera atgriež vērtību, kas nav nulle, filtra funkcija process() sāk ņemt un apstrādāt datus no bufera vajadzīgā lieluma daļās, līdz tā ir izsmelta.
Dati, kas vēl nav nepieciešami, paliek buferī kā pirmais korteļa elements, kuram tiek pievienoti nākamie ievades datu bloki.

Struktūra, kas apraksta buferi:

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

Funkcijas darbam ar MSBufferizer

Jaunas bufera instances izveide:

MSBufferizer * ms_bufferizer_new(void);

Atmiņa tiek piešķirta, inicializēta ms_bufferizer_init() un tiek atgriezts rādītājs.

Inicializācijas funkcija:

void ms_bufferizer_init(MSBufferizer *obj); 

Rinda tiek inicializēta q, lauks izmērs ir iestatīts uz nulli.

Ziņas pievienošana:

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

Ziņojums m tiek pievienots rindai. Tiek pievienots aprēķinātais datu bloku lielums izmērs.

Visu ziņojumu pārsūtīšana no saites datu rindas uz buferi q:

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

Ziņojumu pārsūtīšana no saites q buferī tiek veikta, izmantojot funkciju ms_bufferizer_put().

Lasīšana no bufera:

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

Ja buferī uzkrāto datu apjoms ir mazāks par pieprasīto (datalen), tad funkcija atgriež nulli, dati netiek kopēti datos. Pretējā gadījumā tiek veikta datu secīga kopēšana no buferī esošajiem kortežiem. Pēc kopēšanas kortežs tiek izdzēsts un atmiņa tiek atbrīvota. Kopēšana beidzas brīdī, kad tiek kopēti datalen baiti. Ja datu bloka vidū pietrūks vietas, šajā ziņojumā datu bloks tiks samazināts līdz atlikušajai nekopētajai daļai. Nākamreiz zvanot, kopēšana tiks turpināta no šī punkta.

Pašlaik buferī pieejamā datu apjoma nolasīšana:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Atgriež lauku izmērs buferis.

Daļas datu atmešana buferī:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Norādītais datu baitu skaits tiek izgūts un izmests. Vecākie dati tiek izmesti.

Visu buferī esošo ziņojumu dzēšana:

void ms_bufferizer_flush(MSBufferizer *obj); 

Datu skaitītājs tiek atiestatīts uz nulli.

Visu buferī esošo ziņojumu dzēšana:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Skaitītājs netiek atiestatīts.

Bufera noņemšana un atmiņas atbrīvošana:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Bufera izmantošanas piemērus var atrast vairāku multivides straumēšanas filtru avota kodā. Piemēram, MS_L16_ENC filtrā, kas pārkārto baitus paraugos no tīkla pasūtījuma uz resursdatora pasūtījumu: l16.c

Nākamajā rakstā mēs apskatīsim jautājumu par slodžu slodzes novērtēšanu un veidus, kā cīnīties pret pārmērīgu skaitļošanas slodzi multivides straumētājā.

Avots: www.habr.com

Pievieno komentāru