Հոդվածի նյութը վերցված է իմ
Տվյալների շարժման մեխանիզմ
- Տվյալների բլոկ 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.
Հետևյալ նկարը ցույց է տալիս երեք հաղորդագրություններից բաղկացած բազմապատիկ m1, m1_1, m1_2.
Հաղորդագրման գործառույթներ 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:
Հետևյալ նկարը ցույց է տալիս q1 անունով հերթ, որը պարունակում է 4 հաղորդագրություն m1,m2,m3,m4: Հաղորդագրությունը m2-ը բազմակի գլուխն է, որը պարունակում է ևս երկու հաղորդագրություն m2_1 և m2_2:
Հերթերի հետ աշխատելու գործառույթներ 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 ֆիլտրում, որը նմուշների բայթերը վերադասավորում է ցանցային կարգից դեպի հյուրընկալող կարգ.
Հաջորդ հոդվածում մենք կանդրադառնանք ցուցիչի ծանրաբեռնվածության գնահատման խնդրին և մեդիա հոսքի ավելորդ հաշվողական բեռի դեմ պայքարելու ուղիներին:
Source: www.habr.com