Mediastreamer2 VoIP кыймылдаткычын изилдөө. 11-бөлүк

Макаланын материалы менин zen каналы.

Mediastreamer2 VoIP кыймылдаткычын изилдөө. 11-бөлүк

Маалыматтардын кыймылынын механизми

  • Маалымат блогу dblk_t
  • Билдирүү mblk_t
  • Билдирүүлөр менен иштөө функциялары mblk_t
  • Кезек queue_t
  • Кезектер менен иштөө функциялары queue_t
  • Чыпкаларды туташтыруу
  • Маалыматтарды иштетүү графигинин сигналдык чекити
  • Тикердин көшөгө артындагы иш-аракеттери
  • Буферизатор (MSBufferizer)
  • MSBufferizer менен иштөө функциялары

Акырында макала биз өзүбүздүн фильтрибизди иштеп чыктык. Бул макалада медиа агым чыпкаларынын ортосунда маалыматтарды жылдыруунун ички механизмине көңүл бурулат. Бул келечекте аз күч менен татаал чыпкаларды жазууга мүмкүндүк берет.

Маалыматтардын кыймылынын механизми

Медиа агымдагы маалыматтардын кыймылы структура тарабынан сүрөттөлгөн кезектердин жардамы менен ишке ашырылат queue_t. сыяктуу билдирүүлөрдүн саптары mblk_t, алар өздөрү сигнал маалыматтарын камтыбайт, бирок мурунку, кийинки билдирүүгө жана маалымат блогуна шилтемелер гана. Мындан тышкары, мен өзгөчө белгилеп кетким келет, ошондой эле бир эле түрдөгү билдирүүгө шилтеме үчүн талаа бар, ал билдирүүлөрдүн жалгыз шилтемеленген тизмесин уюштурууга мүмкүндүк берет. Мындай тизме менен бириктирилген билдирүүлөр тобун кортеж деп атайбыз. Ошентип, кезектин каалаган элементи бир билдирүү болушу мүмкүн mblk_t, жана балким, билдирүү кортежинин башчысы mblk_t. Ар бир кортеждик билдирүү өзүнүн бөлүм маалымат блогуна ээ болушу мүмкүн. Эмне үчүн кортеждер керек экенин биз бир аздан кийин талкуулайбыз.

Жогоруда айтылгандай, билдирүүнүн өзү маалымат блогун камтыбайт; анын ордуна, ал блок сакталган эстутум аймагына көрсөткүчтү гана камтыйт. Бул бөлүктө медиа стримердин ишинин жалпы сүрөтү "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);

Киргизүү параметри катары блок сактай турган маалыматтардын өлчөмү берилет. Бөлүнгөн эс-тутумдун башына башты - структураны коюу үчүн көбүрөөк эс бөлүнөт datab. Бирок башка функцияларды колдонгондо, бул дайыма эле боло бербейт, кээ бир учурларда, маалымат буфери маалымат блогунун аталышынан өзүнчө жайгашышы мүмкүн. Түзүмдү түзүүдө талаалар анын талаасы болуп конфигурацияланат 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_prev, b_келеси, алар эки эселенген тизмени уюштуруу үчүн зарыл (бул кезек queue_t).

Андан кийин көрсөткүч келет 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 белгиленген өлчөмдөгү маалымат блогу менен көлөм, экинчи аргумент - ПРИ китепкананын бул версиясында колдонулган эмес. Ал нөл бойдон калууга тийиш. Функциянын иштөө учурунда жаңы билдирүүнүн структурасы үчүн эс тутум бөлүнөт жана функция чакырылат 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. Бул учурда, билдирүү кортежге айланат.

Эгер кортежге дагы бир маалымат блогун кошуу керек болсо, анда функцияны колдонушуңуз керек:

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

ал кортежден акыркы билдирүүнү табат (алда b_cont нөл болот) жана бул билдирүү үчүн функцияны чакырат appendb().

Функцияны колдонуу менен билдирүүдөгү же кортеждеги маалыматтардын көлөмүн биле аласыз:

int msgdsize(const mblk_t *mp);

ал кортеждеги бардык билдирүүлөрдү айланып өтөт жана ал билдирүүлөрдүн маалымат буферлериндеги маалыматтардын жалпы көлөмүн кайтарат. Ар бир билдирүү үчүн маалыматтардын көлөмү төмөнкүдөй эсептелет:

 mp->b_wptr - mp->b_rptr

Эки кортежди бириктирүү үчүн функцияны колдонуңуз:

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

ал кортежди кошот newm түтүктүн куйругуна mp жана натыйжадагы кортеждин акыркы билдирүүсүнө көрсөткүчтү кайтарат.

Зарыл болсо, кортежди бир маалымат блогу менен бир билдирүүгө айландырууга болот, бул функция тарабынан аткарылат:

void msgpullup(mblk_t *mp,int len);

аргумент болсо Лен -1 болсо, анда бөлүнгөн буфердин өлчөмү автоматтык түрдө аныкталат. Эгерде Лен оң сан болсо, бул өлчөмдөгү буфер түзүлөт жана кортеждик кабардын маалыматтары ага көчүрүлөт. Буфер бүтсө, көчүрүү ошол жерде токтойт. Кортеждин биринчи билдирүүсү көчүрүлгөн маалыматтар менен жаңы өлчөмдөгү буферди алат. Калган билдирүүлөр жок кылынат жана эстутум үймөгө кайтарылат.

Түзүмдү жок кылууда 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);

билдирүү 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);

Аргумент болсо Лен -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);

буруп, queue_t

Медиа агымдагы билдирүү кезеги тегерек эки жолу байланышкан тизме катары ишке ашырылат. Ар бир тизме элементи сигнал үлгүлөрү менен маалымат блогуна көрсөткүчтү камтыйт. Көрсө, маалыматтар блогунун көрсөткүчтөрү гана кезеги менен жылат, ал эми маалымат өзү кыймылсыз бойдон калат. Ошол. аларга шилтемелер гана жылдырылат.
Кезекти сүрөттөгөн структура queue_t, Төмөндө көрсөтүлгөн:

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

Түзүм талааны камтыйт - көрсөткүч _q_стоппер *mblk_t териңиз, ал кезектеги биринчи элементти (билдирүү) көрсөтөт. Түзүмдүн экинчи талаасы кезектеги билдирүүлөрдүн эсептегичи.
Төмөнкү сүрөттө m1, m4, m1, m2 3 билдирүүнү камтыган q4 деп аталган кезек көрсөтүлгөн.
Mediastreamer2 VoIP кыймылдаткычын изилдөө. 11-бөлүк
Төмөнкү сүрөттө m1,m4,m1,m2 3 билдирүүнү камтыган q4 деп аталган кезек көрсөтүлгөн. m2 билдирүүсү дагы эки m2_1 жана m2_2 билдирүүлөрдү камтыган кортеждин башы.

Mediastreamer2 VoIP кыймылдаткычын изилдөө. 11-бөлүк

Кезектер менен иштөө функциялары queue_t

Кезекти баштоо:

void qinit(queue_t *q);

талаа _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 элементтин алдында киргизилген ЭБП. эгер ЭБП=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, м) билдирүүгө нөл көрсөткүчү менен функция циклдер. Программаңыз катып калат. окшош мамиле кылат ms_queue_next(q, м).

Чыпкаларды туташтыруу

Жогоруда сүрөттөлгөн кезек билдирүүлөрдү бир чыпкадан экинчисине же биринен бир нече чыпкага өткөрүү үчүн колдонулат. Чыпкалар жана алардын байланыштары багытталган графикти түзөт. Фильтрдин кириши же чыгышы жалпы сөз "пин" деп аталат. Чыпкалардын бири-бирине туташтырылган тартибин сүрөттөө үчүн медиа агымчы "сигнал чекити" түшүнүгүн колдонот. Сигнал пункту структура болуп саналат _MSCPoint, анда чыпкага көрсөткүч жана анын бир төөнөгүчүнүн номери камтылган; ошого жараша ал чыпканын кириш же чыгууларынын биринин байланышын сүрөттөйт.

Маалыматтарды иштетүү графигинин сигналдык чекити

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 программасындагы граф фильтрине туташтырганда, биз тикерге азыртан баштап аны өчүргөнчө башкара турган графикти көрсөтөбүз. Туташкандан кийин тикер өзүнө жүктөлгөн графикти изилдеп, аны камтыган чыпкалардын тизмесин түзө баштайт. Бир эле чыпканы эки жолу “эсептебеш” үчүн, ал табылган чыпкаларды аларга белги коюу менен белгилейт. көргөн. Издөө ар бир фильтрдеги шилтеме таблицаларын колдонуу менен жүргүзүлөт.

График менен таанышуу учурунда тикер чыпкалардын арасында маалымат блокторунун булагы болуп иштеген жок дегенде бирөө бар же жок экенин текшерет. Эгерде алар жок болсо, анда график туура эмес болуп эсептелет жана тикер бузулат.

Эгерде график "туура" болуп чыкса, ар бир табылган чыпка үчүн функция инициализацияга чакырылат preprocess(). Кийинки иштетүү циклине учур келгенде (демейки боюнча ар бир 10 миллисекундда) тикер функцияны чакырат. процесс() бардык мурда табылган булак чыпкалары үчүн, андан кийин тизмедеги калган чыпкалар үчүн. Эгерде чыпкада киргизүү шилтемелери болсо, анда функцияны иштетиңиз процесс() киргизүү шилтеме кезектери бош болмоюнча кайталанат. Андан кийин, ал тизмедеги кийинки чыпкага өтөт жана киргизүү шилтемелеринде билдирүүлөр жок болмоюнча, аны "сыдырат". Тикер тизме аяктаганга чейин чыпкадан чыпкага жылат. Бул циклди иштетүүнү аяктайт.

Эми биз кортеждерге кайтып келип, медиа стримерге эмне үчүн мындай объект кошулганы жөнүндө сүйлөшөбүз. Жалпысынан алганда, чыпка ичинде иштеген алгоритм талап кылынган маалыматтардын көлөмү дал келбейт жана киргизүүдө алынган маалымат буферлеринин өлчөмүнө эсе көп эмес. Мисалы, биз Фурьенин тез трансформациясын аткарган чыпканы жазып жатабыз, анын аныктамасы боюнча өлчөмү экиден турган маалымат блокторун гана иштете алат. 512 саны болсун. Эгерде маалыматтар телефон каналы тарабынан түзүлсө, анда киргизүүдөгү ар бир билдирүүнүн буфери бизге 160 сигнал үлгүсүн алып келет. Керектүү көлөмдөгү маалымат болмоюнча, киргизүүдөн маалыматтарды чогултбоо азгырык. Бирок бул учурда тикер менен кагылышуу пайда болот, ал фильтрди киргизүү шилтемеси бош болмоюнча сыдырууга ийгиликсиз аракет кылат. Мурда биз бул эрежени чыпкалоонун үчүнчү принциби катары белгилегенбиз. Бул принципке ылайык, фильтрдин process() функциясы киргизүү кезектериндеги бардык маалыматтарды алышы керек.

Мындан тышкары, киргизүүдөн 512 гана үлгү алуу мүмкүн эмес, анткени сиз бүтүндөй блокторду гана ала аласыз, б.а. чыпка 640 үлгүлөрдү алып, алардын 512син колдонушу керек, калганы маалыматтардын жаңы бөлүгүн топтоодон мурун. Ошентип, биздин чыпка, анын негизги ишине тышкары, киргизилген маалыматтарды аралык сактоо үчүн көмөкчү иш-аракеттерди камсыз кылуу керек. Медиа стримерди иштеп чыгуучулар жана бул жалпы маселени чечүү үчүн атайын объектти - MSBufferizer (буферер) иштелип чыккан, ал бул маселени кортеждердин жардамы менен чечет.

Буферизатор (MSBufferizer)

Бул чыпка алгоритмин иштетүү үчүн маалыматтын көлөмү жетиштүү болгондо, чыпка ичинде киргизилген маалыматтарды топтой турган объект болуп саналат. Буфер маалыматтарды топтоп жатканда, чыпка процессордун иштетүү күчүн колдонбостон, бош режимде иштейт. Бирок буферден окуу функциясы нөлдөн башка маанини кайтарар замат, фильтрдин process() функциясы буферден керектүү өлчөмдөгү бөлүкчөлөрдөгү маалыматтарды, ал түгөнгөнгө чейин алып, иштеп баштайт.
Азырынча талап кылынбаган маалыматтар кортеждин биринчи элементи катары буферде калат, ага киргизүү маалыматтарынын кийинки блоктору тиркелет.

Буферди сүрөттөгөн структура:

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); 

Эгерде буферде топтолгон маалыматтардын көлөмү суралгандан аз болсо (datalen), анда функция нөлдү кайтарат, маалыматтар маалыматтарга көчүрүлбөйт. Болбосо буферде жайгашкан кортеждерден маалыматтарды ырааттуу көчүрүү жүргүзүлөт. Көчүрүүдөн кийин кортеж жок кылынат жана эс тутум бошотулат. Көчүрүү маалымат байттары көчүрүлгөн учурда аяктайт. Эгерде маалымат блогунун ортосунда бош орун түгөнүп калса, анда бул билдирүүдө маалымат блогу көчүрүлбөгөн калган бөлүгүнө кыскартылат. Кийинки жолу чалганыңызда, көчүрүү ушул жерден уланат.

Учурда буферде жеткиликтүү болгон маалыматтардын көлөмүн окуу:

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

Комментарий кошуу