„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis

Straipsnio medžiaga paimta iš mano zen kanalas.

„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis

Duomenų judėjimo mechanizmas

  • Duomenų blokas dblk_t
  • Pranešimas mblk_t
  • Funkcijos, skirtos darbui su pranešimais mblk_t
  • Eilė eilė_t
  • Funkcijos, skirtos darbui su eilėmis queue_t
  • Filtrų prijungimas
  • Duomenų apdorojimo grafiko signalinis taškas
  • Tikerio veikla užkulisiuose
  • Buferis (MSBufferizer)
  • Funkcijos, skirtos darbui su MSBufferizer

Paskutiniame straipsnis sukūrėme savo filtrą. Šiame straipsnyje daugiausia dėmesio bus skiriama vidiniam duomenų perkėlimo tarp medijos srautinių filtrų mechanizmui. Taip ateityje galėsite rašyti sudėtingus filtrus su mažiau pastangų.

Duomenų judėjimo mechanizmas

Duomenų judėjimas medijos sraute atliekamas naudojant struktūras aprašytas eiles eilė_t. Žinučių eilutės kaip mblk_t, kuriose nėra signalo duomenų, o tik nuorodos į ankstesnį, kitą pranešimą ir į duomenų bloką. Be to, noriu ypač pabrėžti, kad taip pat yra laukas nuorodai į to paties tipo pranešimą, kuris leidžia tvarkyti atskirai susietą pranešimų sąrašą. Pranešimų grupę, kurią vienija toks sąrašas, vadinsime kortežu. Taigi, bet kuris eilės elementas gali būti vienas pranešimas mblk_t, o gal ir žinučių eilutės galva mblk_t. Kiekvienas kortelių pranešimas gali turėti savo palatos duomenų bloką. Kodėl reikia kortelių, aptarsime šiek tiek vėliau.

Kaip minėta aukščiau, pačiame pranešime nėra duomenų bloko; vietoj to jame yra tik rodyklė į atminties sritį, kurioje saugomas blokas. Šioje dalyje bendras žiniasklaidos transliuotojo darbo vaizdas primena animacinio filmo „Monsters, Inc.“ durų sandėlį, kur durys (nuorodos į duomenis - kambariai) juda beprotišku greičiu viršutiniais konvejeriais, o pačios patalpos. likti nejudantis.

Dabar, judėdami hierarchija iš apačios į viršų, išsamiai apsvarstykime išvardytus duomenų perdavimo mechanizmo objektus medijos sraute.

Duomenų blokas dblk_t

Duomenų blokas susideda iš antraštės ir duomenų buferio. Antraštė aprašyta tokia struktūra,

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

Struktūros laukuose yra rodyklės į buferio pradžią, buferio pabaigą ir duomenų buferio ištrynimo funkciją. Paskutinis elementas antraštėje db_ref — atskaitos skaitiklis, jei jis pasiekia nulį, tai yra signalas ištrinti šį bloką iš atminties. Jei duomenų bloką sukūrė funkcija datab_alloc() , tada duomenų buferis bus patalpintas į atmintį iš karto po antraštės. Visais kitais atvejais buferis gali būti kur nors atskirai. Duomenų buferyje bus signalų pavyzdžiai arba kiti duomenys, kuriuos norime apdoroti filtrais.

Naujas duomenų bloko egzempliorius sukuriamas naudojant funkciją:

dblk_t *datab_alloc(int size);

Kaip įvesties parametras nurodomas duomenų, kuriuos blokas saugos, dydis. Paskirstoma daugiau atminties, kad skirtos atminties pradžioje būtų antraštė – struktūra duomenųb. Tačiau naudojant kitas funkcijas, tai ne visada atsitinka; kai kuriais atvejais duomenų buferis gali būti atskirai nuo duomenų bloko antraštės. Kuriant struktūrą laukai sukonfigūruojami taip, kad jos laukas db_base nurodė duomenų srities pradžią ir db_lim iki jos pabaigos. Nuorodų skaičius db_ref nustatytas į vieną. Duomenų išvalymo funkcijos žymeklis nustatytas į nulį.

Žinutė mblk_t

Kaip minėta, eilės elementai yra tipo mblk_t, jis apibrėžiamas taip:

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 pradžioje yra nuorodų b_ankstesnis, b_kitas, kurios yra būtinos norint organizuoti dvigubai susietą sąrašą (tai yra eilė eilė_t).

Tada ateina rodyklė b_cont, kuris naudojamas tik tada, kai pranešimas yra eilutės dalis. Paskutiniam sekoje esančiam pranešimui šis žymeklis lieka nulis.

Toliau matome žymeklį į duomenų bloką b_datap, kuriam skirtas pranešimas. Po jo seka rodyklės į sritį, esančią bloko duomenų buferyje. Laukas b_rptr nurodo vietą, iš kurios bus nuskaitomi duomenys iš buferio. Laukas b_wptr nurodo vietą, iš kurios bus rašoma į buferį.

Likę laukai yra paslaugų pobūdžio ir nesusiję su duomenų perdavimo mechanizmo veikimu.

Žemiau yra vienas pranešimas su pavadinimu m1 ir duomenų blokas d1.
„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis
Toliau pateiktame paveikslėlyje parodyta trijų pranešimų rinkinys m1, m1_1, m1_2.
„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis

Žinučių siuntimo funkcijos mblk_t

Nauja žinutė mblk_t sukurta pagal funkciją:

mblk_t *allocb(int size, int pri); 

ji įdeda naują žinutę atmintyje mblk_t su nurodyto dydžio duomenų bloku Dydis, antras argumentas - pri nenaudojamas šioje bibliotekos versijoje. Turėtų likti nulis. Funkcijos veikimo metu naujo pranešimo struktūrai bus skirta atmintis ir funkcija bus iškviesta mblk_init(), kuri iš naujo nustatys visus sukurto struktūros egzemplioriaus laukus ir tada naudodama aukščiau paminėtą datab_alloc(), sukurs duomenų buferį. Po to struktūros laukai bus sukonfigūruoti:

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

Išvestyje gauname naują pranešimą su inicijuotais laukais ir tuščiu duomenų buferiu. Norėdami pridėti duomenų prie pranešimo, turite nukopijuoti jį į duomenų bloko buferį:

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

kur duomenys yra rodyklė į duomenų šaltinį ir Dydis - jų dydis.
tada reikia atnaujinti žymeklį į rašymo tašką, kad jis vėl nukreiptų į laisvos buferio srities pradžią:

msg->b_wptr = msg->b_wptr + size

Jei jums reikia sukurti pranešimą iš esamo buferio, nekopijuojant, naudokite funkciją:

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

Funkcija, sukūrusi pranešimą ir duomenų bloko struktūrą, sukonfigūruos savo nuorodas į duomenis adresu mėgėjas. Tie. šiuo atveju duomenų buferis nėra po duomenų bloko antraštės laukais, kaip buvo kuriant duomenų bloką su funkcija datab_alloc(). Buferis su funkcijai perduotais duomenimis išliks ten, kur buvo, tačiau rodyklių pagalba bus priklijuotas prie naujai sukurtos duomenų bloko antraštės, o tai atitinkamai ir prie pranešimo.

Į vieną žinutę mblk_t Keli duomenų blokai gali būti sujungti nuosekliai. Tai atliekama naudojant funkciją:

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

mp — pranešimas, prie kurio bus pridėtas kitas duomenų blokas;
duomenys — žymeklis į bloką, kurio kopija bus pridėta prie pranešimo;
Dydis — duomenų dydis;
padas — vėliavėlė, kad skirtos atminties dydis turi būti išlygintas išilgai 4 baitų ribos (papildymas bus atliktas su nuliais).

Jei esamame pranešimų duomenų buferyje yra pakankamai vietos, nauji duomenys bus įklijuoti už jau esančių duomenų. Jei pranešimo duomenų buferyje yra mažiau laisvos vietos nei Dydis, tada sukuriamas naujas pranešimas su pakankamo buferio dydžiu ir duomenys nukopijuojami į jo buferį. Tai naujas pranešimas, susietas su pradiniu naudojant žymeklį b_cont. Tokiu atveju pranešimas virsta eilute.

Jei jums reikia pridėti kitą duomenų bloką į seką, turite naudoti funkciją:

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

ji suras paskutinę žinutę eilutėje (jis turi b_cont bus nulinis) ir iškvies šio pranešimo funkciją apendb().

Duomenų dydį žinutėje ar rinkinyje galite sužinoti naudodami funkciją:

int msgdsize(const mblk_t *mp);

jis peržiūrės visus sekoje esančius pranešimus ir grąžins bendrą duomenų kiekį tų pranešimų duomenų buferiuose. Kiekvieno pranešimo duomenų kiekis apskaičiuojamas taip:

 mp->b_wptr - mp->b_rptr

Norėdami sujungti dvi eilutes, naudokite funkciją:

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

ji prideda eilutę newm į uodegą mp ir grąžina žymeklį į paskutinį gautos eilės pranešimą.

Jei reikia, eilė gali būti paversta vienu pranešimu su vienu duomenų bloku; tai atlieka funkcija:

void msgpullup(mblk_t *mp,int len);

jei argumentas len yra -1, tada paskirstyto buferio dydis nustatomas automatiškai. Jeigu len yra teigiamas skaičius, bus sukurtas tokio dydžio buferis ir į jį bus nukopijuoti kortelių pranešimo duomenys. Jei baigsis buferis, kopijavimas sustos. Pirmasis kortelių pranešimas gaus naujo dydžio buferį su nukopijuotais duomenimis. Likę pranešimai bus ištrinti, o atmintis grąžinama į krūvą.

Naikinant struktūrą mblk_t į duomenų bloko atskaitos skaičių atsižvelgiama, jei skambinant freeb () pasirodo, kad jis yra nulis, tada duomenų buferis ištrinamas kartu su egzemplioriumi mblk_t, o tai nurodo.

Naujo pranešimo laukų inicijavimas:

void mblk_init(mblk_t *mp);

Kito duomenų dalies pridėjimas prie pranešimo:

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

Jei nauji duomenys netelpa į laisvą pranešimų duomenų buferio erdvę, tada prie pranešimo pridedamas atskirai sukurtas pranešimas su reikiamo dydžio buferiu (pirmame pranešime nustatomas žymeklis į pridėtą pranešimą) ir žinutė virsta eilute.

Duomenų dalies įtraukimas į seką:

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

Funkcija appendb() iškviečia ciklas.

Dviejų eilučių sujungimas į vieną:

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

Žinutė newm bus prijungtas prie mp.

Vieno pranešimo kopijos kūrimas:

mblk_t *copyb(const mblk_t *mp);

Visiškas eilės su visais duomenų blokais kopijavimas:

mblk_t *copymsg(const mblk_t *mp);

Kortelės elementus nukopijuoja funkcija copyb ().

Sukurkite paprastą pranešimo kopiją mblk_t. Šiuo atveju duomenų blokas nėra kopijuojamas, bet padidinamas jo atskaitos skaitiklis db_ref:

mblk_t *dupb(mblk_t *mp);

Lengvos kortos kopijos kūrimas. Duomenų blokai nėra kopijuojami, tik didinami jų atskaitos skaitikliai db_ref:

mblk_t *dupmsg(mblk_t* m);

Visų eilės pranešimų sujungimas į vieną pranešimą:

void msgpullup(mblk_t *mp,size_t len);

Jei argumentas len yra -1, tada paskirstyto buferio dydis nustatomas automatiškai.

Laiško trynimas, eilutė:

void freemsg(mblk_t *mp);

Duomenų bloko nuorodų skaičius sumažinamas vienu. Jei jis pasiekia nulį, duomenų blokas taip pat ištrinamas.

Bendro duomenų kiekio žinutėje arba eilutėje apskaičiavimas.

size_t msgdsize(const mblk_t *mp);

Pranešimo gavimas iš eilės galo:

mblk_t *ms_queue_peek_last (q);

Vieno pranešimo rezervuotų laukų turinio kopijavimas į kitą pranešimą (iš tikrųjų šiuose laukuose yra vėliavėlės, kurias naudoja medijos transliuotojas):

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

Eilė eilė_t

Pranešimų eilė medijos transliuotoje įdiegta kaip apskritas, dvigubai susietas sąrašas. Kiekviename sąrašo elemente yra rodyklė į duomenų bloką su signalų pavyzdžiais. Pasirodo, paeiliui juda tik rodyklės į duomenų bloką, o patys duomenys lieka nejudantys. Tie. perkeliamos tik nuorodos į juos.
Eilę apibūdinanti struktūra eilė_t, nurodyta apačioje:

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

Struktūroje yra laukas – rodyklė _q_stopper įveskite *mblk_t, jis nurodo pirmąjį elementą (pranešimą) eilėje. Antrasis struktūros laukas yra pranešimų eilėje skaitiklis.
Žemiau esančiame paveikslėlyje parodyta eilė pavadinimu q1, kurioje yra 4 pranešimai m1, m2, m3, m4.
„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis
Toliau pateiktame paveikslėlyje parodyta eilė pavadinimu q1, kurioje yra 4 pranešimai m1,m2,m3,m4. Pranešimas m2 yra eilutės, kurioje yra dar du pranešimai m2_1 ir m2_2, galvutė.

„Mediastreamer2 VoIP“ variklio tyrinėjimas. 11 dalis

Funkcijos, skirtos darbui su eilėmis queue_t

Eilės inicijavimas:

void qinit(queue_t *q);

Laukas _q_stopper (toliau tai vadinsime „kamščiu“) inicijuoja funkcija mblk_init(), jo ankstesnis elementas ir kito elemento rodyklė yra pakoreguoti taip, kad nukreiptų į save. Eilės elementų skaitiklis iš naujo nustatomas į nulį.

Naujo elemento pridėjimas (pranešimai):

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

Naujas elementas m įtrauktas į sąrašo pabaigą, elementų rodyklės sureguliuojamos taip, kad kamštis taptų kitu jo elementu, o jis taptų ankstesniu kamščio elementu. Eilės elementų skaitiklis padidinamas.

Elemento gavimas iš eilės:

mblk_t * getq(queue_t *q); 

Nuskaitomas pranešimas, kuris ateina po kamščio, o elementų skaitiklis sumažinamas. Jei eilėje nėra elementų, išskyrus stabdiklį, grąžinamas 0.

Pranešimo įterpimas į eilę:

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

Elementas mp įterptas prieš elementą EMP. Jei EMP=0, tada pranešimas įtraukiamas į eilės galą.

Pranešimo gavimas iš eilės viršaus:

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

Elementų skaitiklis sumažinamas.

Rodyklės skaitymas į pirmąjį eilės elementą:

mblk_t * peekq(queue_t *q); 

Visų elementų pašalinimas iš eilės ištrinant pačius elementus:

void flushq(queue_t *q, int how);

Argumentas kaip nėra naudojamas. Eilės elementų skaitiklis nustatytas į nulį.

Makrokomandas, skirtas nuskaityti žymiklį į paskutinį eilės elementą:

mblk_t * qlast(queue_t *q);

Dirbdami su pranešimų eilėmis, atminkite, kad skambindami ms_queue_put(q, m) su nuline žymekliu į pranešimą, funkcija kilpos. Jūsų programa užstos. elgiasi panašiai ms_queue_next(q, m).

Filtrų prijungimas

Aukščiau aprašyta eilė naudojama pranešimams perduoti iš vieno filtro į kitą arba iš vieno filtro į kelis. Filtrai ir jų jungtys sudaro nukreiptą grafiką. Filtro įvestis arba išvestis bus vadinami bendruoju žodžiu „pin“. Norėdami apibūdinti filtrų sujungimo tvarką, medijos transliuotojas naudoja „signalo taško“ sąvoką. Signalo taškas yra struktūra _MSCPoint, kuriame yra rodyklė į filtrą ir vieno jo kaiščio numeris; atitinkamai jis apibūdina vieno iš filtro įėjimų arba išėjimų prijungimą.

Duomenų apdorojimo grafiko signalinis taškas

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

Filtro kaiščiai numeruojami pradedant nuo nulio.

Dviejų kontaktų sujungimas pranešimų eilėje aprašomas struktūra _MSQueue, kuriame yra pranešimų eilė ir nuorodos į du signalo taškus, kuriuos jis jungia:

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

Šią struktūrą vadinsime signalo nuoroda. Kiekviename medijos srauto filtre yra įvesties nuorodų lentelė ir išvesties nuorodų lentelė (MSQueue). Lentelių dydis nustatomas kuriant filtrą; mes tai jau padarėme naudodami eksportuotą tipo kintamąjį MSFilterDesc, kai sukūrėme savo filtrą. Žemiau pateikiama struktūra, apibūdinanti bet kokį medijos srauto filtrą, 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;

Sujungę filtrus programoje C pagal savo planą (bet neprijungę žymeklio), taip sukūrėme nukreiptą grafiką, kurio mazgai yra struktūros pavyzdžiai. MSFilter, o kraštai yra nuorodų pavyzdžiai MSQueue.

Tikerio veikla užkulisiuose

Kai sakiau, kad ticker yra erkių šaltinio filtras, tai nebuvo visa tiesa. Stulpelis yra objektas, vykdantis laikrodžio funkcijas procesas () visi grandinės (grafiko), prie kurios jis prijungtas, filtrai. Kai C programoje sujungiame žymeklį prie grafiko filtro, rodome stulpeliui grafiką, kurį jis valdys nuo šiol, kol jį išjungsime. Prisijungęs, ticker pradeda nagrinėti jam patikėtą grafiką, sudarydamas filtrų, kuriuose jis yra, sąrašą. Kad tas pats filtras nebūtų „skaičiuojamas“ du kartus, jis pažymi aptiktus filtrus, įdėdamas į juos žymimąjį laukelį matyti. Paieška atliekama naudojant nuorodų lenteles, kurias turi kiekvienas filtras.

Per savo įvadinį grafiko turą ticker patikrina, ar tarp filtrų yra bent vienas, kuris veikia kaip duomenų blokų šaltinis. Jei jų nėra, grafikas laikomas neteisingu ir žymeklis sugenda.

Jei grafikas pasirodo esąs „teisingas“, kiekvienam rastam filtrui funkcija iškviečiama inicijuoti išankstinis apdorojimas (). Kai tik ateina momentas kitam apdorojimo ciklui (pagal numatytuosius nustatymus kas 10 milisekundžių), žymeklis iškviečia funkciją procesas () visiems anksčiau rastiems šaltinio filtrams ir likusiems sąraše esantiems filtrams. Jei filtras turi įvesties nuorodas, paleiskite funkciją procesas () kartojasi tol, kol įvesties nuorodų eilės bus tuščios. Po to jis pereina prie kito sąrašo filtro ir jį „slenka“, kol įvesties nuorodose nebeliks pranešimų. Stulpelis juda iš vieno filtro prie filtro, kol sąrašas baigiasi. Tai užbaigia ciklo apdorojimą.

Dabar grįšime prie kortelių ir kalbėsime apie tai, kodėl toks subjektas buvo įtrauktas į žiniasklaidos srautą. Apskritai filtro viduje veikiančiam algoritmui reikalingas duomenų kiekis nesutampa ir nėra įėjime gaunamų duomenų buferių dydžio kartotinis. Pavyzdžiui, mes rašome filtrą, kuris atlieka greitą Furjė transformaciją, kuri pagal apibrėžimą gali apdoroti tik duomenų blokus, kurių dydis yra dviejų laipsnis. Tegul tai bus 512. Jei duomenys generuojami telefono kanalu, kiekvieno pranešimo duomenų buferis įėjime atneš 160 signalų pavyzdžių. Kyla pagunda nerinkti duomenų iš įvesties, kol nėra reikiamo duomenų kiekio. Tačiau šiuo atveju įvyks susidūrimas su žymekliu, kuris nesėkmingai bandys slinkti filtrą, kol įvesties nuoroda bus tuščia. Anksčiau šią taisyklę paskyrėme trečiuoju filtro principu. Pagal šį principą filtro procesas() funkcija turi paimti visus duomenis iš įvesties eilių.

Be to, iš įvesties nebus galima paimti tik 512 mėginių, nes galite paimti tik ištisus blokus, t.y. filtras turės paimti 640 mėginių ir panaudoti 512 iš jų, likusią dalį prieš kaupdamas naują duomenų dalį. Taigi mūsų filtras, be savo pagrindinio darbo, turi numatyti pagalbinius veiksmus tarpiniam įvesties duomenų saugojimui. Media streamer ir šios bendros problemos sprendimo kūrėjai sukūrė specialų objektą - MSBufferizer (buferis), kuris išsprendžia šią problemą naudodamas korteles.

Buferis (MSBufferizer)

Tai objektas, kuris kaups įvesties duomenis filtro viduje ir pradės pateikti juos apdoroti, kai tik informacijos pakaks filtro algoritmui paleisti. Kol buferis kaupia duomenis, filtras veiks tuščiosios eigos režimu, nenaudodamas procesoriaus apdorojimo galios. Tačiau kai tik nuskaitymo funkcija iš buferio grąžina kitą reikšmę nei nulis, filtro funkcija process() pradeda imti ir apdoroti duomenis iš buferio reikiamo dydžio dalimis, kol ji bus išnaudota.
Duomenys, kurie dar nereikalingi, lieka buferyje kaip pirmasis rinkinio elementas, prie kurio pridedami tolesni įvesties duomenų blokai.

Buferį apibūdinanti struktūra:

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

Funkcijos, skirtos darbui su MSBufferizer

Naujo buferio egzemplioriaus kūrimas:

MSBufferizer * ms_bufferizer_new(void);

Atmintis paskirstoma, inicijuojama ms_bufferizer_init() ir grąžinama rodyklė.

Inicijavimo funkcija:

void ms_bufferizer_init(MSBufferizer *obj); 

Eilė inicijuojama q, laukas Dydis yra nustatytas į nulį.

Pranešimo pridėjimas:

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

Žinutė m įtraukta į eilę. Pridedamas apskaičiuotas duomenų blokų dydis Dydis.

Visų pranešimų perkėlimas iš nuorodos duomenų eilės į buferį q:

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

Pranešimų perkėlimas iš nuorodos q buferyje atliekama naudojant funkciją ms_bufferizer_put().

Skaitymas iš buferio:

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

Jei buferyje sukauptų duomenų dydis yra mažesnis nei prašomas (datalen), tada funkcija grąžina nulį, duomenys į duomenis nenukopijuojami. Kitu atveju atliekamas nuoseklus duomenų kopijavimas iš buferyje esančių kortelių. Po kopijavimo seka ištrinama ir atlaisvinama atmintis. Kopijavimas baigiamas tuo metu, kai nukopijuojami duomenų baitai. Jei duomenų bloko viduryje pritrūksta vietos, šiame pranešime duomenų blokas bus sumažintas iki likusios nenukopijuotos dalies. Kai kitą kartą skambinsite, kopijavimas bus tęsiamas nuo šio taško.

Šiuo metu buferyje esančių duomenų kiekio skaitymas:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Grąžina lauką Dydis buferis.

Dalies duomenų atmetimas buferyje:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Nurodytas duomenų baitų skaičius yra nuskaitomas ir atmetamas. Seniausi duomenys atmetami.

Visų pranešimų ištrynimas buferyje:

void ms_bufferizer_flush(MSBufferizer *obj); 

Duomenų skaitiklis atstatomas į nulį.

Visų pranešimų ištrynimas buferyje:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Skaitiklis nenustatytas iš naujo.

Buferio pašalinimas ir atminties atlaisvinimas:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Buferio naudojimo pavyzdžius galima rasti kelių medijos srauto filtrų šaltinio kode. Pavyzdžiui, MS_L16_ENC filtre, kuris pertvarko baitus pavyzdžiuose iš tinklo tvarkos į pagrindinio kompiuterio tvarką: l16.c

Kitame straipsnyje apžvelgsime stulpelio apkrovos įvertinimo problemą ir tai, kaip susidoroti su per didele kompiuterine apkrova medijos sraute.

Šaltinis: www.habr.com

Добавить комментарий