Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11

Հոդվածի նյութը վերցված է իմ զեն ալիք.

Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11

Տվյալների շարժման մեխանիզմ

  • Տվյալների բլոկ dblk_t
  • Հաղորդագրություն mblk_t
  • mblk_t հաղորդագրությունների հետ աշխատելու գործառույթներ
  • Հերթ հերթ_t
  • Հերթերի հետ աշխատելու գործառույթներ queue_t
  • Զտիչների միացում
  • Տվյալների մշակման գրաֆիկի ազդանշանային կետը
  • Տիկերի կուլիսային գործունեությունը
  • Բուֆերիզատոր (MSBufferizer)
  • MSBufferizer-ի հետ աշխատելու գործառույթներ

Անցյալում Հոդված մենք մշակել ենք մեր սեփական ֆիլտրը: Այս հոդվածը կկենտրոնանա մեդիա հոսքի ֆիլտրերի միջև տվյալների տեղափոխման ներքին մեխանիզմի վրա: Սա թույլ կտա ձեզ ապագայում ավելի քիչ ջանքերով բարդ զտիչներ գրել:

Տվյալների շարժման մեխանիզմ

Տվյալների տեղաշարժը մեդիա հոսքերում կատարվում է կառուցվածքի կողմից նկարագրված հերթերի միջոցով հերթ_տ. Հաղորդագրությունների տողեր, ինչպիսիք են mblk_t, որոնք իրենք չեն պարունակում ազդանշանային տվյալներ, այլ միայն հղումներ դեպի նախորդ, հաջորդ հաղորդագրությունը և տվյալների բլոկին: Բացի այդ, ես ուզում եմ հատկապես ընդգծել, որ կա նաև դաշտ նույն տեսակի հաղորդագրության հղման համար, որը թույլ է տալիս կազմակերպել հաղորդագրությունների առանձին կապակցված ցուցակ: Նման ցուցակով միավորված հաղորդագրությունների խումբը մենք կկոչենք կրկնակի։ Այսպիսով, հերթի ցանկացած տարր կարող է լինել մեկ հաղորդագրություն mblk_t, և միգուցե հաղորդագրության բազմակի ղեկավարը mblk_t. Յուրաքանչյուր բազմակի հաղորդագրություն կարող է ունենալ ծխի տվյալների իր բլոկը: Մենք կքննարկենք, թե ինչու են tuples-ները անհրաժեշտ մի փոքր ուշ:

Ինչպես նշվեց վերևում, հաղորդագրությունն ինքնին չի պարունակում տվյալների բլոկ, փոխարենը այն պարունակում է միայն ցուցիչ դեպի հիշողության տարածքը, որտեղ պահվում է բլոկը: Այս մասում մեդիա հեռարձակողի աշխատանքի ընդհանուր պատկերը հիշեցնում է «Monsters, Inc.» մուլտֆիլմի դռան պահեստը, որտեղ դռները (տվյալների հղումներ - սենյակներ) շարժվում են խելահեղ արագությամբ վերևի փոխակրիչներով, մինչդեռ սենյակներն իրենք են։ մնալ անշարժ.

Այժմ, շարժվելով հիերարխիայի երկայնքով ներքևից վերև, եկեք մանրամասն դիտարկենք տվյալների փոխանցման մեխանիզմի թվարկված սուբյեկտները մեդիա հոսքի մեջ:

Տվյալների բլոկ dblk_t

Տվյալների բլոկը բաղկացած է վերնագրից և տվյալների բուֆերից: Վերնագիրը նկարագրված է հետևյալ կառուցվածքով.

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

Կառուցվածքի դաշտերը պարունակում են ցուցիչներ դեպի բուֆերի սկիզբը, բուֆերի վերջը և տվյալների բուֆերի ջնջման գործառույթը։ Վերնագրի վերջին տարրը db_ref — հղման հաշվիչը, եթե այն հասնում է զրոյի, սա ազդանշան է ծառայում այս բլոկը հիշողությունից ջնջելու համար: Եթե ​​տվյալների բլոկը ստեղծվել է ֆունկցիայի կողմից datab_alloc() , ապա տվյալների բուֆերը կտեղադրվի հիշողության մեջ՝ վերնագրից անմիջապես հետո։ Մնացած բոլոր դեպքերում բուֆերը կարող է տեղակայվել ինչ-որ տեղ առանձին: Տվյալների բուֆերը կպարունակի ազդանշանի նմուշներ կամ այլ տվյալներ, որոնք մենք ցանկանում ենք մշակել զտիչներով:

Տվյալների բլոկի նոր օրինակ է ստեղծվում՝ օգտագործելով գործառույթը.

dblk_t *datab_alloc(int size);

Որպես մուտքային պարամետր, տրվում է այն տվյալների չափը, որը բլոկը կպահի: Ավելի շատ հիշողություն է հատկացվում հատկացված հիշողության սկզբում վերնագիր - կառուցվածքը տեղադրելու համար տվյալների շտեմարան. Բայց այլ գործառույթներ օգտագործելիս դա միշտ չէ, որ տեղի է ունենում, որոշ դեպքերում տվյալների բուֆերը կարող է տեղակայվել տվյալների բլոկի վերնագրից առանձին: Կառուցվածք ստեղծելիս դաշտերը կազմաձևվում են այնպես, որ դրա դաշտը db_base մատնանշեց տվյալների տարածքի սկիզբը, և db_lim մինչև իր վերջը։ Հղումների քանակը db_ref սահմանված է մեկ: Տվյալների մաքրման գործառույթի ցուցիչը դրված է զրոյի:

Հաղորդագրություն mblk_t

Ինչպես նշվեց, հերթի տարրերը տիպի են mblk_t, այն սահմանվում է հետևյալ կերպ.

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;

Կառուցվածք mblk_t սկզբում պարունակում է ցուցիչներ բ_նախ, b_next, որոնք անհրաժեշտ են կրկնակի կապակցված ցուցակ կազմակերպելու համար (որը հերթ է հերթ_տ).

Հետո գալիս է ցուցիչը b_cont, որն օգտագործվում է միայն այն դեպքում, երբ հաղորդագրությունը բազմակի մաս է: Թուպլիկի վերջին հաղորդագրության համար այս ցուցիչը մնում է չեղյալ:

Հաջորդը մենք տեսնում ենք տվյալների բլոկի ցուցիչ b_datap, որի համար հաղորդագրությունը գոյություն ունի: Դրան հաջորդում են բլոկի տվյալների բուֆերի ներսում գտնվող տարածքի ցուցիչները: Դաշտ b_rptr նշում է այն վայրը, որտեղից կկարդան բուֆերի տվյալները: Դաշտ b_wptr ցույց է տալիս այն վայրը, որտեղից կկատարվեն գրություններ բուֆերում:

Մնացած դաշտերը ծառայողական բնույթ են կրում և չեն վերաբերում տվյալների փոխանցման մեխանիզմի աշխատանքին:

Ստորև բերված է մեկ հաղորդագրություն անունով m1 և տվյալների բլոկ d1.
Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11
Հետևյալ նկարը ցույց է տալիս երեք հաղորդագրություններից բաղկացած բազմապատիկ m1, m1_1, m1_2.
Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11

Հաղորդագրման գործառույթներ mblk_t

Նոր հաղորդագրություն mblk_t ստեղծված գործառույթով.

mblk_t *allocb(int size, int pri); 

նա նոր հաղորդագրություն է դնում հիշատակին mblk_t նշված չափի տվյալների բլոկով Չափս, երկրորդ փաստարկ - pri գրադարանի այս տարբերակում չի օգտագործվում: Այն պետք է մնա զրո: Ֆունկցիայի գործարկման ընթացքում հիշողությունը կհատկացվի նոր հաղորդագրության կառուցվածքին և ֆունկցիան կկանչվի mblk_init (), որը կզրոյացնի կառույցի ստեղծված օրինակի բոլոր դաշտերը, այնուհետև՝ օգտագործելով վերը նշվածը datab_alloc(), կստեղծի տվյալների բուֆեր: Դրանից հետո կառուցվածքի դաշտերը կկարգավորվեն.

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

Ելքում մենք ստանում ենք նոր հաղորդագրություն՝ նախնականացված դաշտերով և դատարկ տվյալների բուֆերով: Հաղորդագրությանը տվյալներ ավելացնելու համար հարկավոր է այն պատճենել տվյալների բլոկի բուֆերում՝

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

որտեղ տվյալներ տվյալների աղբյուրի ցուցիչ է, և Չափս - դրանց չափը.
ապա դուք պետք է թարմացնեք ցուցիչը գրելու կետին, որպեսզի այն նորից մատնանշի բուֆերի ազատ տարածքի սկիզբը.

msg->b_wptr = msg->b_wptr + size

Եթե ​​Ձեզ անհրաժեշտ է հաղորդագրություն ստեղծել գոյություն ունեցող բուֆերից՝ առանց պատճենելու, ապա օգտագործեք ֆունկցիան.

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

Գործառույթը, հաղորդագրությունը և տվյալների բլոկի կառուցվածքը ստեղծելուց հետո, կկազմաձևի իր ցուցիչները տվյալ հասցեի վրա: պայթել. Նրանք. այս դեպքում տվյալների բուֆերը չի գտնվում տվյալների բլոկի վերնագրի դաշտերից հետո, ինչպես դա եղել է ֆունկցիայի հետ տվյալների բլոկ ստեղծելիս: datab_alloc(). Գործառույթին փոխանցված տվյալների հետ բուֆերը կմնա այնտեղ, որտեղ եղել է, բայց ցուցիչների օգնությամբ այն կկցվի տվյալների բլոկի նորաստեղծ վերնագրին և, համապատասխանաբար, հաղորդագրությանը։

Մեկ հաղորդագրության mblk_t Տվյալների մի քանի բլոկներ կարող են հաջորդաբար միացվել: Սա կատարվում է ֆունկցիայի միջոցով.

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

mp — հաղորդագրություն, որին կավելացվի մեկ այլ տվյալների բլոկ.
տվյալներ — բլոկի ցուցիչ, որի պատճենը կավելացվի հաղորդագրությանը.
Չափս - տվյալների չափը;
պահոց — դրոշակ, որ հատկացված հիշողության չափը պետք է հավասարեցվի 4 բայթ սահմանի երկայնքով (լրացումը կկատարվի զրոներով):

Եթե ​​առկա հաղորդագրությունների տվյալների բուֆերում բավականաչափ տեղ կա, ապա նոր տվյալները կտեղադրվեն արդեն այնտեղ գտնվող տվյալների հետևում: Եթե ​​հաղորդագրության տվյալների բուֆերում ավելի քիչ ազատ տարածք կա, քան Չափս, այնուհետև ստեղծվում է նոր հաղորդագրություն՝ բավարար բուֆերային չափով և տվյալները պատճենվում են դրա բուֆերում։ Սա նոր հաղորդագրություն է, որը կապված է բնօրինակի հետ՝ օգտագործելով ցուցիչ b_cont. Այս դեպքում հաղորդագրությունը վերածվում է բազմակի։

Եթե ​​Ձեզ անհրաժեշտ է տվյալների ևս մեկ բլոկ ավելացնել tuple-ին, ապա դուք պետք է օգտագործեք գործառույթը.

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

նա կգտնի վերջին հաղորդագրությունը բազմակի մեջ (նա ունի b_cont կլինի null) և կկանչի գործառույթը այս հաղորդագրության համար հավելված ().

Դուք կարող եք պարզել հաղորդագրության կամ բազմակի տվյալների չափը՝ օգտագործելով գործառույթը.

int msgdsize(const mblk_t *mp);

այն կշրջանցի բազմակի բոլոր հաղորդագրությունները և կվերադարձնի տվյալների ընդհանուր քանակը այդ հաղորդագրությունների տվյալների բուֆերներում: Յուրաքանչյուր հաղորդագրության համար տվյալների քանակը հաշվարկվում է հետևյալ կերպ.

 mp->b_wptr - mp->b_rptr

Երկու tuples միավորելու համար օգտագործեք գործառույթը.

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

նա կցում է բազմոցը նորեկ դեպի պոչը տուպլ mp և վերադարձնում է ցուցիչը ստացված բազմակի վերջին հաղորդագրությանը:

Անհրաժեշտության դեպքում, բազմապատիկը կարող է վերածվել մեկ հաղորդագրության տվյալների մեկ բլոկով, դա արվում է ֆունկցիայի միջոցով.

void msgpullup(mblk_t *mp,int len);

եթե փաստարկ len -1 է, ապա հատկացված բուֆերի չափը որոշվում է ավտոմատ կերպով: Եթե len դրական թիվ է, կստեղծվի այս չափի բուֆեր և կրկնօրինակված հաղորդագրությունների տվյալները դրա մեջ: Եթե ​​բուֆերը սպառվի, պատճենումն այնտեղ կդադարի: Tuple-ի առաջին հաղորդագրությունը կստանա նոր չափի բուֆեր՝ պատճենված տվյալների հետ: Մնացած հաղորդագրությունները կջնջվեն, իսկ հիշողությունը կվերադառնա կույտ:

Կառույցը ջնջելիս mblk_t տվյալների բլոկի տեղեկանքների քանակը հաշվի է առնվում, եթե զանգահարելիս freeb () պարզվում է, որ զրո է, ապա տվյալների բուֆերը ջնջվում է օրինակի հետ միասին mblk_t, որը մատնանշում է դա։

Նոր հաղորդագրության դաշտերի սկզբնավորում.

void mblk_init(mblk_t *mp);

Հաղորդագրությանը մեկ այլ տվյալների ավելացում.

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

Եթե ​​նոր տվյալները չեն տեղավորվում հաղորդագրության տվյալների բուֆերի ազատ տարածության մեջ, ապա հաղորդագրությանը կցվում է առանձին ստեղծված հաղորդագրություն՝ պահանջվող չափի բուֆերով (առաջին հաղորդագրության մեջ նշված է ավելացված հաղորդագրության ցուցիչը) և հաղորդագրությունը վերածվում է բազմակի:

Տվյալների կտոր ավելացնելով բազմակի մեջ.

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

Ֆունկցիան կանչում է appendb() հանգույցը:

Երկու բուկլետների միավորում մեկում.

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

Հաղորդագրություն նորեկ կկցվի mp.

Մեկ հաղորդագրության կրկնօրինակում.

mblk_t *copyb(const mblk_t *mp);

Տվյալների բոլոր բլոկներով բազմակի ամբողջական պատճենում.

mblk_t *copymsg(const mblk_t *mp);

Թուպլիկի տարրերը պատճենվում են ֆունկցիայով copyb ().

Ստեղծեք հաղորդագրության հեշտ պատճեն mblk_t. Այս դեպքում տվյալների բլոկը չի պատճենվում, բայց դրա հղման հաշվիչը մեծանում է db_ref:

mblk_t *dupb(mblk_t *mp);

Թուպլիկի թեթև կրկնօրինակի պատրաստում: Տվյալների բլոկները չեն պատճենվում, ավելացվում են միայն դրանց հղման հաշվիչները db_ref:

mblk_t *dupmsg(mblk_t* m);

Բազմաթիվ բոլոր հաղորդագրությունների սոսնձումը մեկ հաղորդագրության մեջ.

void msgpullup(mblk_t *mp,size_t len);

Եթե ​​փաստարկը len -1 է, ապա հատկացված բուֆերի չափը որոշվում է ավտոմատ կերպով:

Հաղորդագրության ջնջում, կրկնապատկում.

void freemsg(mblk_t *mp);

Տվյալների բլոկի տեղեկանքների քանակը նվազում է մեկով: Եթե ​​այն հասնում է զրոյի, ապա տվյալների բլոկը նույնպես ջնջվում է:

Հաղորդագրության կամ բազմակի տվյալների ընդհանուր քանակի հաշվարկ:

size_t msgdsize(const mblk_t *mp);

Հերթի պոչից հաղորդագրություն ստանալը.

mblk_t *ms_queue_peek_last (q);

Մեկ հաղորդագրության վերապահված դաշտերի բովանդակությունը մեկ այլ հաղորդագրության մեջ պատճենելը (իրականում այս դաշտերը պարունակում են դրոշներ, որոնք օգտագործվում են մեդիա հոսքի կողմից).

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

Հերթ հերթ_տ

Հաղորդագրությունների հերթը մեդիա հոսքի մեջ իրականացվում է որպես շրջանաձև կրկնակի կապակցված ցուցակ: Ցանկի յուրաքանչյուր տարր պարունակում է ազդանշանի նմուշներով տվյալների բլոկի ցուցիչ: Պարզվում է, որ հերթով շարժվում են միայն տվյալների բլոկի ցուցիչները, մինչդեռ տվյալներն ինքնին մնում են անշարժ։ Նրանք. տեղափոխվում են միայն դրանց հղումները:
Հերթը նկարագրող կառուցվածք հերթ_տ, ստորև ներկայացված է.

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

Կառուցվածքը պարունակում է դաշտ՝ ցուցիչ _ք_խցան մուտքագրեք *mblk_t, այն ցույց է տալիս հերթի առաջին տարրը (հաղորդագրությունը): Կառույցի երկրորդ դաշտը հերթում գտնվող հաղորդագրությունների հաշվիչն է:
Ստորև բերված նկարը ցույց է տալիս q1 անունով հերթ, որը պարունակում է 4 հաղորդագրություն m1, m2, m3, m4:
Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11
Հետևյալ նկարը ցույց է տալիս q1 անունով հերթ, որը պարունակում է 4 հաղորդագրություն m1,m2,m3,m4: Հաղորդագրությունը m2-ը բազմակի գլուխն է, որը պարունակում է ևս երկու հաղորդագրություն m2_1 և m2_2:

Ուսումնասիրելով Mediastreamer2 VoIP շարժիչը: Մաս 11

Հերթերի հետ աշխատելու գործառույթներ queue_t

Հերթի սկզբնավորում.

void qinit(queue_t *q);

Դաշտ _ք_խցան (այսուհետև մենք այն կանվանենք «խցան») սկզբնավորվում է ֆունկցիայով mblk_init (), նրա նախորդ տարրը և հաջորդ տարրի ցուցիչը ճշգրտվում են, որպեսզի մատնանշեն իրեն: Հերթի տարրի հաշվիչը զրոյացված է:

Նոր տարրի ավելացում (հաղորդագրություններ).

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

Նոր տարր m ավելացվում է ցանկի վերջում, տարրերի ցուցիչները ճշգրտվում են այնպես, որ խցանը դառնա նրա հաջորդ տարրը, իսկ այն՝ խցանի նախորդ տարրը: Հերթի տարրի հաշվիչը ավելանում է:

Տարրը հերթից հանելը.

mblk_t * getq(queue_t *q); 

Հաղորդագրությունը, որը գալիս է խցանից հետո, վերցվում է, և տարրի հաշվիչը նվազում է: Եթե ​​հերթում բացառությամբ խցանման տարրեր չկան, ապա վերադարձվում է 0:

Հաղորդագրության տեղադրում հերթում.

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

Էլ mp տեղադրված տարրից առաջ EMP. Եթե EMP=0, այնուհետև հաղորդագրությունն ավելացվում է հերթի վերջում:

Հերթի ղեկավարից հաղորդագրություն ստանալը.

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

Տարրերի հաշվիչը նվազում է:

Հերթի առաջին տարրի ցուցիչը կարդալը.

mblk_t * peekq(queue_t *q); 

Հեռացնելով բոլոր տարրերը հերթից՝ ջնջելով տարրերն իրենք.

void flushq(queue_t *q, int how);

փաստարկ ինչպես չի օգտագործվում. Հերթի տարրի հաշվիչը դրված է զրոյի:

Մակրո՝ հերթի վերջին տարրի ցուցիչը կարդալու համար.

mblk_t * qlast(queue_t *q);

Հաղորդագրությունների հերթերի հետ աշխատելիս նկատի ունեցեք, որ երբ զանգահարեք ms_queue_put(q, m) հաղորդագրության զրոյական ցուցիչով ֆունկցիան պտտվում է: Ձեր ծրագիրը կսառչի: իրեն նույն կերպ է պահում ms_հերթ_հաջորդ(ք, մ).

Զտիչների միացում

Վերևում նկարագրված հերթը օգտագործվում է հաղորդագրությունները մի զտիչից մյուսը կամ մեկից մի քանի զտիչներ փոխանցելու համար: Զտիչները և դրանց միացումները կազմում են ուղղորդված գրաֆիկ: Ֆիլտրի մուտքը կամ ելքը կկոչվի «փին» ընդհանուր բառ: Զտիչների միացման հաջորդականությունը նկարագրելու համար մեդիա հոսքագիծն օգտագործում է «ազդանշանի կետ» հասկացությունը: Ազդանշանի կետը կառուցվածքն է _MSCPpoint, որը պարունակում է ցուցիչ դեպի ֆիլտրը և դրա կապանքներից մեկի համարը, համապատասխանաբար, այն նկարագրում է ֆիլտրի մուտքերից կամ ելքերից մեկի միացումը։

Տվյալների մշակման գրաֆիկի ազդանշանային կետը

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

Ֆիլտրի քորոցները համարակալված են զրոյից սկսած:

Հաղորդագրությունների հերթով երկու կապի միացումը նկարագրված է կառուցվածքով _MSQueue, որը պարունակում է հաղորդագրությունների հերթ և ցուցիչներ դեպի այն երկու ազդանշանային կետերը, որոնք նա կապում է.

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

Մենք այս կառույցը կանվանենք ազդանշանային կապ: Մեդիա հոսքի յուրաքանչյուր զտիչ պարունակում է մուտքային հղումների աղյուսակ և ելքային հղումների աղյուսակ (MSQueue) Աղյուսակների չափերը սահմանվում են զտիչ ստեղծելու ժամանակ, մենք դա արդեն արել ենք՝ օգտագործելով արտահանված տեսակի փոփոխական MSFilterDesc, երբ մենք մշակեցինք մեր սեփական ֆիլտրը: Ստորև բերված է մի կառուցվածք, որը նկարագրում է ցանկացած զտիչ մեդիա հոսքի մեջ, 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;

Այն բանից հետո, երբ մենք միացրինք C ծրագրում ֆիլտրերը մեր պլանին համապատասխան (բայց չմիացրինք նշիչը), մենք դրանով ստեղծեցինք ուղղորդված գրաֆիկ, որի հանգույցները կառուցվածքի օրինակներ են: MSFilter, իսկ եզրերը հղումների օրինակներ են MSQueue.

Տիկերի կուլիսային գործունեությունը

Երբ ես ձեզ ասացի, որ տիզը ֆիլտր է տիզերի աղբյուրի համար, դա ամբողջ ճշմարտությունը չէր դրա մասին: Տիկերը այն օբյեկտն է, որն իրականացնում է գործառույթներ ժամացույցի վրա գործընթաց () շղթայի (գրաֆիկի) բոլոր զտիչները, որոնց այն միացված է: Երբ մենք միացնում ենք տիկերը գրաֆիկական ֆիլտրին C ծրագրում, մենք տիկերին ցույց ենք տալիս այն գրաֆիկը, որը նա կվերահսկի այսուհետ, մինչև մենք այն անջատենք: Միանալուց հետո տիկերը սկսում է ուսումնասիրել իրեն վստահված գրաֆիկը՝ կազմելով այն ներառող զտիչների ցանկը։ Նույն ֆիլտրը երկու անգամ «չհաշվելու» համար այն նշում է հայտնաբերված ֆիլտրերը՝ դրանցում վանդակ դնելով։ նկատել. Որոնումն իրականացվում է հղումների աղյուսակների միջոցով, որոնք ունեն յուրաքանչյուր զտիչ:

Գրաֆիկի ներածական շրջագայության ընթացքում ցուցիչը ստուգում է, թե արդյոք զտիչների մեջ կա առնվազն մեկը, որը հանդես է գալիս որպես տվյալների բլոկների աղբյուր: Եթե ​​չկան, ապա գրաֆիկը համարվում է սխալ, և նշանը խափանում է:

Եթե ​​գրաֆիկը պարզվում է, որ «ճիշտ» է, ապա յուրաքանչյուր հայտնաբերված ֆիլտրի համար ֆունկցիան կանչվում է սկզբնավորման համար նախնական գործընթաց (). Հենց որ գալիս է հաջորդ մշակման ցիկլի պահը (կանխադրված յուրաքանչյուր 10 միլիվայրկյան), ցուցիչը կանչում է ֆունկցիան գործընթաց () բոլոր նախկինում հայտնաբերված աղբյուրի զտիչների համար, այնուհետև ցանկի մնացած զտիչների համար: Եթե ​​ֆիլտրն ունի մուտքագրման հղումներ, ապա գործարկեք գործառույթը գործընթաց () կրկնվում է այնքան ժամանակ, մինչև մուտքագրման հղման հերթերը դատարկվեն: Դրանից հետո այն անցնում է ցուցակի հաջորդ զտիչին և «ոլորում» այն, մինչև մուտքագրված հղումները զերծ լինեն հաղորդագրություններից: Նշիչը շարժվում է զտիչից զտիչ, մինչև ցուցակն ավարտվի: Սա ավարտում է ցիկլի մշակումը:

Այժմ մենք կանդրադառնանք tuples-ին և կխոսենք այն մասին, թե ինչու է նման սուբյեկտը ավելացվել մեդիա հոսքի մեջ: Ընդհանուր առմամբ, ֆիլտրի ներսում գործող ալգորիթմի կողմից պահանջվող տվյալների քանակը չի համընկնում և չի հանդիսանում մուտքում ստացված տվյալների բուֆերների չափի բազմապատիկ: Օրինակ, մենք գրում ենք ֆիլտր, որն իրականացնում է արագ Ֆուրիեի փոխակերպում, որը, ըստ սահմանման, կարող է մշակել միայն տվյալների բլոկներ, որոնց չափը երկու ուժ է: Թող լինի 512 հաշիվ։ Եթե ​​տվյալները գեներացվում են հեռախոսային ալիքով, ապա մուտքի յուրաքանչյուր հաղորդագրության տվյալների բուֆերը մեզ կբերի ազդանշանի 160 նմուշ: Գայթակղիչ է չհավաքել տվյալներ մուտքագրումից, քանի դեռ անհրաժեշտ քանակությամբ տվյալներ չկան: Բայց այս դեպքում բախում տեղի կունենա տիկերի հետ, որը անհաջող կերպով կփորձի ոլորել զտիչը, մինչև մուտքագրման հղումը դատարկ լինի: Նախկինում մենք այս կանոնը նշանակեցինք որպես ֆիլտրի երրորդ սկզբունք: Այս սկզբունքի համաձայն, ֆիլտրի process() ֆունկցիան պետք է վերցնի բոլոր տվյալները մուտքային հերթերից:

Բացի այդ, մուտքից հնարավոր չի լինի վերցնել միայն 512 նմուշ, քանի որ կարող եք վերցնել միայն ամբողջական բլոկներ, այսինքն. ֆիլտրը պետք է վերցնի 640 նմուշ և օգտագործի դրանցից 512-ը, մնացածը՝ մինչև տվյալների նոր մասը կուտակելը։ Այսպիսով, մեր զտիչը, բացի իր հիմնական աշխատանքից, պետք է ապահովի օժանդակ գործողություններ մուտքային տվյալների միջանկյալ պահպանման համար: Մեդիա հոսքի մշակողները և այս ընդհանուր խնդրի լուծումը մշակել են հատուկ օբյեկտ՝ MSBufferizer (բուֆերեր), որը լուծում է այս խնդիրը՝ օգտագործելով tuples։

Բուֆերիզատոր (MSBufferizer)

Սա մի օբյեկտ է, որը կկուտակի մուտքային տվյալները ֆիլտրի ներսում և կսկսի դրանք մշակել, հենց որ ինֆորմացիայի քանակը բավարար լինի զտիչի ալգորիթմը գործարկելու համար: Մինչ բուֆերը կուտակում է տվյալներ, ֆիլտրը կգործի անգործուն ռեժիմում՝ առանց պրոցեսորի վերամշակման հզորությունը սպառելու: Բայց հենց որ բուֆերից կարդալու ֆունկցիան վերադարձնում է զրոյից տարբեր արժեք, ֆիլտրի process() ֆունկցիան սկսում է տվյալներ վերցնել և մշակել բուֆերից պահանջվող չափի մասերով, մինչև դրանք սպառվեն:
Տվյալները, որոնք դեռևս չեն պահանջվում, մնում են բուֆերում՝ որպես tuple-ի առաջին տարր, որին կցվում են մուտքային տվյալների հետագա բլոկները:

Կառուցվածքը, որը նկարագրում է բուֆերը.

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

MSBufferizer-ի հետ աշխատելու գործառույթներ

Նոր բուֆերային օրինակի ստեղծում.

MSBufferizer * ms_bufferizer_new(void);

Հիշողությունը հատկացվում է, սկզբնավորվում է ms_bufferizer_init() և ցուցիչը վերադարձվում է:

Նախնականացման գործառույթ.

void ms_bufferizer_init(MSBufferizer *obj); 

Հերթը սկզբնավորվում է q, դաշտ Չափս դրված է զրոյի:

Հաղորդագրության ավելացում.

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

Հերթին ավելացվում է m հաղորդագրություն: Տվյալների բլոկների հաշվարկված չափը ավելացվում է Չափս.

Բոլոր հաղորդագրությունները հղումի տվյալների հերթից դեպի բուֆեր տեղափոխելը q:

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

Հաղորդագրությունների փոխանցում հղումից q բուֆերում կատարվում է ֆունկցիայի միջոցով ms_bufferizer_put().

Բուֆերից կարդալը.

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

Եթե ​​բուֆերում կուտակված տվյալների չափը պահանջվածից փոքր է (դատալեն), այնուհետև ֆունկցիան վերադարձնում է զրո, տվյալները չեն պատճենվում տվյալներին: Հակառակ դեպքում, կատարվում է տվյալների հաջորդական պատճենում բուֆերում գտնվող tuples-ից: Պատճենելուց հետո բազմությունը ջնջվում է և հիշողությունն ազատվում է: Պատճենահանումն ավարտվում է այն պահին, երբ պատճենվում են տվյալների բայթերը: Եթե ​​տվյալների բլոկի մեջտեղում տարածքը սպառվում է, ապա այս հաղորդագրության մեջ տվյալների բլոկը կկրճատվի մինչև չպատճենված մնացած մասը: Հաջորդ անգամ, երբ զանգահարեք, պատճենումը կշարունակվի այս կետից:

Բուֆերում ներկայումս առկա տվյալների քանակի ընթերցում.

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Վերադարձնում է դաշտը Չափս բուֆերատոր.

Բուֆերում տվյալների մի մասի հեռացում.

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Տվյալների նշված բայթերի քանակը վերցվում և հեռացվում է: Ամենահին տվյալները հանված են:

Բուֆերում գտնվող բոլոր հաղորդագրությունների ջնջում.

void ms_bufferizer_flush(MSBufferizer *obj); 

Տվյալների հաշվիչը զրոյացված է:

Բուֆերում գտնվող բոլոր հաղորդագրությունների ջնջում.

void ms_bufferizer_uninit(MSBufferizer *obj); 

Հաշվիչը չի վերակայվում:

Բուֆերի հեռացում և հիշողության ազատում.

void ms_bufferizer_destroy(MSBufferizer *obj);  

Բուֆերատորի օգտագործման օրինակները կարելի է գտնել մի քանի մեդիա հոսքի զտիչների սկզբնաղբյուրում: Օրինակ՝ MS_L16_ENC ֆիլտրում, որը նմուշների բայթերը վերադասավորում է ցանցային կարգից դեպի հյուրընկալող կարգ. l16.c

Հաջորդ հոդվածում մենք կանդրադառնանք ցուցիչի ծանրաբեռնվածության գնահատման խնդրին և մեդիա հոսքի ավելորդ հաշվողական բեռի դեմ պայքարելու ուղիներին:

Source: www.habr.com

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