Mediastreamer2 VoIP қозғалтқышын зерттеу. 11 бөлім

Мақала материалы менің сайтымнан алынды zen арнасы.

Mediastreamer2 VoIP қозғалтқышын зерттеу. 11 бөлім

Мәліметтерді тасымалдау механизмі

  • Деректер блогы dblk_t
  • Хабарлама mblk_t
  • Хабарламалармен жұмыс істеу функциялары mblk_t
  • Кезек_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);

Енгізу параметрі ретінде оған блок сақтайтын деректердің өлшемі беріледі. Бөлінген жадтың басына тақырыпты - құрылымды орналастыру үшін көбірек жад бөлінеді деректерб. Бірақ басқа функцияларды пайдаланған кезде бұл әрқашан бола бермейді, кейбір жағдайларда деректер буфері деректер блогының тақырыбынан бөлек орналасуы мүмкін. Құрылымды құру кезінде өрістер оның өрісі болатындай конфигурацияланады 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_алдыңғы, b_келесі, олар екі есе байланыстырылған тізімді ұйымдастыру үшін қажет (бұл кезек queue_t).

Содан кейін көрсеткіш келеді b_жалғасы, ол хабарлама кортеждің бөлігі болған кезде ғана пайдаланылады. Кортеждегі соңғы хабарлама үшін бұл көрсеткіш бос болып қалады.

Әрі қарай деректер блогына көрсеткішті көреміз b_datap, ол үшін хабар бар. Одан кейін блок деректер буферінің ішіндегі аймаққа көрсеткіштер келеді. Өріс b_rptr буфердегі деректер оқылатын орынды көрсетеді. Өріс b_wptr буферге жазу орындалатын орынды көрсетеді.

Қалған өрістер сервистік сипатқа ие және деректерді беру механизмінің жұмысына қатысты емес.

Төменде аты бар жалғыз хабарлама берілген m1 және деректер блогы d1.
Mediastreamer2 VoIP қозғалтқышын зерттеу. 11 бөлім
Келесі суретте үш хабарламадан тұратын кортеж көрсетілген m1, м1_1, м1_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_жалғасы. Бұл жағдайда хабарлама кортежге айналады.

Кортежге басқа деректер блогын қосу қажет болса, функцияны пайдалану керек:

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

ол кортеждегі соңғы хабарламаны табады (ол бар b_жалғасы нөл болады) және осы хабарлама үшін функцияны шақырады appendb().

Функцияны пайдаланып хабарламадағы немесе кортеждегі деректердің өлшемін білуге ​​болады:

int msgdsize(const mblk_t *mp);

ол кортеждегі барлық хабарларды айналдырады және сол хабарлардың деректер буферлеріндегі деректердің жалпы көлемін қайтарады. Әрбір хабарлама үшін деректер көлемі келесідей есептеледі:

 mp->b_wptr - mp->b_rptr

Екі кортежді біріктіру үшін функцияны пайдаланыңыз:

mblk_t *concatb(mblk_t *mp, mblk_t *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);

хабар жаңа бекітілетін болады 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, m) Хабарға нөлдік көрсеткішпен функция циклдар жасайды. Сіздің бағдарламаңыз қатып қалады. ұқсас әрекет етеді ms_queue_келесі(q, м).

Сүзгілерді қосу

Жоғарыда сипатталған кезек хабарларды бір сүзгіден екіншісіне немесе біреуден бірнеше сүзгіге жіберу үшін қолданылады. Сүзгілер және олардың байланыстары бағытталған графикті құрайды. Сүзгінің кірісі немесе шығысы жалпы «pin» сөзі деп аталады. Сүзгілердің бір-бірімен қосылу ретін сипаттау үшін медиа ағынды құралы «сигнал нүктесі» түсінігін пайдаланады. Сигнал нүктесі – құрылым _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 миллисекунд сайын) жеткен бойда тикер функцияны шақырады. процесс() бұрын табылған барлық бастапқы сүзгілер үшін, содан кейін тізімдегі қалған сүзгілер үшін. Сүзгінің енгізу сілтемелері болса, функцияны іске қосыңыз процесс() кіріс сілтемесі кезегі бос болғанша қайталанады. Осыдан кейін ол тізімдегі келесі сүзгіге өтеді және кіріс сілтемелерінде хабарлар болмағанша оны «айналдырады». Тикер тізім аяқталғанша сүзгіден сүзгіге ауысады. Бұл циклды өңдеуді аяқтайды.

Енді біз кортеждерге ораламыз және медиа стримерге мұндай нысан неліктен қосылғаны туралы сөйлесеміз. Тұтастай алғанда, сүзгі ішінде жұмыс істейтін алгоритмге қажетті деректер көлемі сәйкес келмейді және кірісте алынған деректер буферлерінің өлшеміне еселік емес. Мысалы, біз анықтамасы бойынша өлшемі екінің дәрежесіне тең деректер блоктарын ғана өңдей алатын жылдам Фурье түрлендіруін орындайтын сүзгіні жазып жатырмыз. 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); 

Егер буферде жинақталған деректердің өлшемі сұралғандан аз болса (деректер), содан кейін функция нөлді қайтарады, деректер деректерге көшірілмейді. Әйтпесе, буферде орналасқан кортеждерден деректерді дәйекті көшіру орындалады. Көшіруден кейін кортеж жойылады және жад босатылады. Көшіру деректер байттары көшірілген кезде аяқталады. Деректер блогының ортасында бос орын бітсе, бұл хабарламада деректер блогы көшірілмеген қалған бөлікке дейін азаяды. Келесі қоңырау шалғанда, көшіру осы сәттен бастап жалғасады.

Қазіргі уақытта буферде қолжетімді деректер көлемін оқу:

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

Келесі мақалада біз тикер жүктемесін бағалау мәселесін және медиа стримерлердегі шамадан тыс есептеу жүктемесін қалай шешуге болатынын қарастырамыз.

Ақпарат көзі: www.habr.com

пікір қалдыру