Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə

Məqalənin materialı məndən götürülmüşdür zen kanalı.

Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə

Məlumatların hərəkət mexanizmi

  • Məlumat bloku dblk_t
  • Mesaj mblk_t
  • Mesajlarla işləmək üçün funksiyalar mblk_t
  • növbə_t
  • queue_t növbələri ilə işləmək üçün funksiyalar
  • Filtrlərin birləşdirilməsi
  • Məlumatların işlənməsi qrafikinin siqnal nöqtəsi
  • Tickerin pərdəarxası fəaliyyətləri
  • Buferizator (MSBufferizer)
  • MSBufferizer ilə işləmək üçün funksiyalar

Sonda məqalə biz öz filtrimizi hazırlamışıq. Bu məqalə media axını filtrləri arasında məlumatların daşınması üçün daxili mexanizmə diqqət yetirəcəkdir. Bu, gələcəkdə daha az səylə mürəkkəb filtrlər yazmağa imkan verəcək.

Məlumatların hərəkət mexanizmi

Media axınında məlumatların hərəkəti struktur tərəfindən təsvir edilən növbələrdən istifadə etməklə həyata keçirilir növbə_t. kimi mesaj sətirləri mblk_tÖzlərində siqnal məlumatı olmayan, yalnız əvvəlki, növbəti mesaja və məlumat blokuna keçidlər. Bundan əlavə, xüsusi olaraq vurğulamaq istəyirəm ki, eyni tipli mesaja keçid üçün bir sahə də var ki, bu da mesajların tək-tək əlaqəli siyahısını təşkil etməyə imkan verir. Belə bir siyahı ilə birləşən mesajlar qrupunu bir dəst adlandıracağıq. Beləliklə, növbənin istənilən elementi tək mesaj ola bilər mblk_t, və bəlkə də mesaj dəstinin rəhbəri mblk_t. Hər bir dəst mesajının öz palata məlumat bloku ola bilər. Tuples niyə lazım olduğunu bir az sonra müzakirə edəcəyik.

Yuxarıda qeyd edildiyi kimi, mesajın özündə məlumat bloku yoxdur, bunun əvəzinə yalnız blokun saxlandığı yaddaş sahəsinə bir göstərici var. Bu hissədə media striminin işinin ümumi mənzərəsi “Monsters, Inc.” cizgi filmindəki qapı anbarını xatırladır, burada qapılar (məlumatlara keçidlər - otaqlar) yerüstü konveyerlər boyunca inanılmaz sürətlə hərəkət edir, otaqların özləri isə hərəkətsiz qalmaq.

İndi iyerarxiya üzrə aşağıdan yuxarıya doğru hərəkət edərək, media axınında məlumat ötürmə mexanizminin sadalanan obyektlərini ətraflı nəzərdən keçirək.

Məlumat bloku dblk_t

Məlumat bloku başlıqdan və məlumat buferindən ibarətdir. Başlıq aşağıdakı quruluşla təsvir olunur:

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

Strukturun sahələrində buferin başlanğıcı, buferin sonu və məlumat buferinin silinməsi funksiyası üçün göstəricilər var. Başlıqdakı son element db_ref — istinad sayğacı, sıfıra çatarsa, bu bloku yaddaşdan silmək üçün siqnal kimi xidmət edir. Əgər verilənlər bloku funksiya tərəfindən yaradılmışdırsa datab_alloc() , onda məlumat buferi başlıqdan dərhal sonra yaddaşa yerləşdiriləcək. Bütün digər hallarda, bufer ayrıca bir yerdə yerləşdirilə bilər. Məlumat buferində filtrlərlə emal etmək istədiyimiz siqnal nümunələri və ya digər məlumatlar olacaq.

Funksiyadan istifadə edərək məlumat blokunun yeni nümunəsi yaradılır:

dblk_t *datab_alloc(int size);

Giriş parametri olaraq blokun saxlayacağı məlumatların ölçüsü verilir. Ayrılmış yaddaşın əvvəlində başlıq - struktur yerləşdirmək üçün daha çox yaddaş ayrılır datab. Ancaq digər funksiyalardan istifadə edərkən bu həmişə baş vermir, bəzi hallarda məlumat buferi məlumat blokunun başlığından ayrıca yerləşdirilə bilər. Struktur yaratarkən sahələr elə konfiqurasiya edilir ki, onun sahəsi db_base məlumat sahəsinin başlanğıcına işarə etdi və db_lim sonuna qədər. Bağlantı sayı db_ref birinə təyin edilir. Məlumatların təmizlənməsi funksiyası göstəricisi sıfıra təyin edilib.

İsmarıc mblk_t

Qeyd edildiyi kimi, növbə elementləri tiplidir mblk_t, aşağıdakı kimi müəyyən edilir:

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;

Struktur mblk_t əvvəlində göstəriciləri ehtiva edir b_əvvəlki, b_növbəti, ikiqat əlaqəli siyahı təşkil etmək üçün zəruri olan (bu növbədir növbə_t).

Sonra göstərici gəlir b_davam, bu yalnız mesaj dəstinin bir hissəsi olduqda istifadə olunur. Tupledakı son mesaj üçün bu göstərici null olaraq qalır.

Sonra məlumat blokuna bir göstərici görürük b_datap, bunun üçün mesaj mövcuddur. Ondan sonra blok məlumat buferinin içindəki sahəyə göstəricilər gəlir. Sahə b_rptr buferdən verilənlərin oxunacağı yeri müəyyən edir. Sahə b_wptr buferə yazıların yerinə yetiriləcəyi yeri göstərir.

Qalan sahələr xidmət xarakteri daşıyır və məlumat ötürmə mexanizminin işləməsi ilə əlaqəli deyil.

Aşağıda adı olan tək bir mesaj var m1 və məlumat bloku d1.
Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə
Aşağıdakı şəkildə üç mesaj dəsti göstərilir m1, m1_1, m1_2.
Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə

Mesajlaşma funksiyaları mblk_t

Yeni mesaj mblk_t funksiyası ilə yaradılmışdır:

mblk_t *allocb(int size, int pri); 

yaddaşına yeni mesaj yerləşdirir mblk_t müəyyən ölçülü məlumat bloku ilə boy, ikinci arqument - pri kitabxananın bu versiyasında istifadə edilmir. Sıfır qalmalıdır. Funksiyanın işləməsi zamanı yeni mesajın strukturu üçün yaddaş ayrılacaq və funksiya çağırılacaq mblk_init(), bu, strukturun yaradılmış nümunəsinin bütün sahələrini sıfırlayacaq və sonra yuxarıda göstərilənlərdən istifadə edərək datab_alloc(), məlumat buferi yaradacaq. Bundan sonra strukturdakı sahələr konfiqurasiya ediləcək:

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

Çıxışda biz işə salınmış sahələr və boş məlumat buferi ilə yeni bir mesaj alırıq. Mesaja məlumat əlavə etmək üçün onu məlumat bloku buferinə köçürməlisiniz:

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

hara məlumat məlumat mənbəyinə işarədir və boy - onların ölçüsü.
onda siz göstəricini yazma nöqtəsinə yeniləməlisiniz ki, o, yenidən buferdəki boş sahənin başlanğıcını göstərsin:

msg->b_wptr = msg->b_wptr + size

Əgər kopyalamadan mövcud buferdən mesaj yaratmaq lazımdırsa, o zaman funksiyadan istifadə edin:

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

Funksiya mesajı və məlumat blokunun strukturunu yaratdıqdan sonra öz göstəricilərini ünvandakı verilənlərə konfiqurasiya edəcəkdir. buff. Bunlar. bu halda, məlumat buferi, funksiyası ilə məlumat bloku yaratarkən olduğu kimi, məlumat blokunun başlıq sahələrindən sonra yerləşmir. datab_alloc(). Funksiyaya ötürülən məlumatların olduğu bufer olduğu yerdə qalacaq, lakin göstəricilərin köməyi ilə məlumat blokunun yeni yaradılmış başlığına və müvafiq olaraq mesaja əlavə olunacaq.

Bir mesaja mblk_t Bir neçə məlumat bloku ardıcıl olaraq birləşdirilə bilər. Bu funksiya ilə edilir:

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

mp — başqa məlumat blokunun əlavə olunacağı mesaj;
məlumat — bloka göstərici, onun surəti mesaja əlavə olunacaq;
boy - verilənlərin ölçüsü;
pad — ayrılmış yaddaşın ölçüsünün 4 baytlıq sərhədə uyğunlaşdırılmalı olduğu bayraq (doldurma sıfırlarla həyata keçiriləcək).

Mövcud mesaj məlumat buferində kifayət qədər yer varsa, yeni məlumatlar artıq orada olan məlumatların arxasına yapışdırılacaq. Mesaj məlumat buferində daha az boş yer varsa boy, sonra kifayət qədər bufer ölçüsü ilə yeni mesaj yaradılır və məlumat onun buferinə kopyalanır. Bu, göstəricidən istifadə edərək orijinalla əlaqələndirilmiş yeni mesajdır b_davam. Bu halda mesaj bir dəftərə çevrilir.

Tuple-a başqa bir məlumat bloku əlavə etmək lazımdırsa, o zaman funksiyadan istifadə etməlisiniz:

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

o, son mesajı dəstdə tapacaq (onun var b_davam null olacaq) və bu mesaj üçün funksiyanı çağıracaq appendb().

Funksiyadan istifadə edərək mesajda və ya dəstdə verilənlərin ölçüsünü öyrənə bilərsiniz:

int msgdsize(const mblk_t *mp);

o, dəstdəki bütün mesajlar arasında dövr edəcək və həmin mesajların məlumat buferlərindəki məlumatların ümumi miqdarını qaytaracaq. Hər bir mesaj üçün məlumatların miqdarı aşağıdakı kimi hesablanır:

 mp->b_wptr - mp->b_rptr

İki dəsti birləşdirmək üçün funksiyadan istifadə edin:

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

o, dəftəri əlavə edir yenim tuple quyruğuna mp və nəticədə yaranan dəstənin son mesajına göstərici qaytarır.

Lazım gələrsə, bir blok məlumat bloku ilə bir mesaja çevrilə bilər; bu funksiya tərəfindən həyata keçirilir:

void msgpullup(mblk_t *mp,int len);

arqument olarsa len -1 olarsa, ayrılmış buferin ölçüsü avtomatik olaraq müəyyən edilir. Əgər len müsbət ədəddirsə, bu ölçüdə bufer yaradılacaq və mesaj dəsti məlumatı ona kopyalanacaq. Bufer bitərsə, kopyalama orada dayanacaq. Tupleın ilk mesajı kopyalanmış məlumatla yeni ölçülü bufer alacaq. Qalan mesajlar silinəcək və yaddaş yığına qaytarılacaq.

Bir strukturu silərkən mblk_t məlumat blokunun istinad sayı, əgər zəng edərkən nəzərə alınır freeb() sıfır olduğu ortaya çıxır, sonra məlumat buferi nümunə ilə birlikdə silinir mblk_t, buna işarə edən.

Yeni mesajın sahələrinin işə salınması:

void mblk_init(mblk_t *mp);

Mesaja başqa bir məlumat parçasının əlavə edilməsi:

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

Əgər yeni məlumatlar mesaj məlumat buferinin boş sahəsinə uyğun gəlmirsə, o zaman mesaja tələb olunan ölçüdə buferlə ayrıca yaradılmış mesaj əlavə edilir (ilk mesajda əlavə edilmiş mesajın göstəricisi qoyulur) və mesaj tuple çevrilir.

Dəstəyə məlumat parçasının əlavə edilməsi:

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

Funksiya bir dövrədə appendb() funksiyasını çağırır.

İki dəsti birinə birləşdirmək:

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

İsmarıc yenim -a əlavə olunacaq mp.

Tək mesajın surətinin çıxarılması:

mblk_t *copyb(const mblk_t *mp);

Bütün məlumat blokları ilə dəftərin tam surətinin çıxarılması:

mblk_t *copymsg(const mblk_t *mp);

Tuple elementləri funksiya tərəfindən kopyalanır copyb().

Mesajın asan surətini yaradın mblk_t. Bu halda, məlumat bloku kopyalanmır, lakin onun istinad sayğacı artır db_ref:

mblk_t *dupb(mblk_t *mp);

Tuplenin yüngül surətinin hazırlanması. Məlumat blokları kopyalanmır, yalnız onların istinad sayğacları artırılır db_ref:

mblk_t *dupmsg(mblk_t* m);

Dəstəyin bütün mesajlarını bir mesaja yapışdırmaq:

void msgpullup(mblk_t *mp,size_t len);

Əgər mübahisə len -1 olarsa, ayrılmış buferin ölçüsü avtomatik olaraq müəyyən edilir.

Mesajın silinməsi, tuple:

void freemsg(mblk_t *mp);

Məlumat blokunun istinad sayı bir azaldılır. Sıfıra çatarsa, məlumat bloku da silinir.

Mesajda və ya dəstdə məlumatların ümumi miqdarının hesablanması.

size_t msgdsize(const mblk_t *mp);

Növbənin quyruğundan mesaj alınır:

mblk_t *ms_queue_peek_last (q);

Bir mesajın qorunan sahələrinin məzmununun digər mesaja kopyalanması (əslində, bu sahələr media yayımçısı tərəfindən istifadə olunan bayraqları ehtiva edir):

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

Növbə növbə_t

Media yayımlayıcısındakı mesaj növbəsi dairəvi ikiqat əlaqəli siyahı kimi həyata keçirilir. Hər bir siyahı elementi siqnal nümunələri olan məlumat blokuna göstəricidən ibarətdir. Məlum olub ki, yalnız verilənlər blokunun göstəriciləri növbə ilə hərəkət edir, məlumatın özü isə hərəkətsiz qalır. Bunlar. yalnız onlara olan keçidlər köçürülür.
Növbəni təsvir edən struktur növbə_t, aşağıda göstərilmişdir:

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

Strukturda bir sahə var - göstərici _q_stoper *mblk_t yazın, növbədəki ilk elementə (mesaj) işarə edir. Strukturun ikinci sahəsi növbədəki mesajların sayğacıdır.
Aşağıdakı şəkildə m1, m4, m1, m2 3 mesajdan ibarət q4 adlı növbə göstərilir.
Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə
Aşağıdakı şəkildə m1, m4, m1, m2 olan 3 mesajdan ibarət q4 adlı növbə göstərilir. m2 mesajı daha iki m2_1 və m2_2 mesajını ehtiva edən dəftərin başıdır.

Mediastreamer2 VoIP mühərriki araşdırılır. 11-ci hissə

queue_t növbələri ilə işləmək üçün funksiyalar

Növbənin işə salınması:

void qinit(queue_t *q);

Sahə _q_stoper (bundan sonra biz onu “stopper” adlandıracağıq) funksiyası ilə işə salınır mblk_init(), onun əvvəlki elementi və növbəti element göstəricisi özünə işarə etmək üçün tənzimlənir. Növbə elementi sayğacı sıfıra sıfırlanır.

Yeni element əlavə etmək (mesajlar):

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

Yeni element m siyahının sonuna əlavə edilir, element göstəriciləri elə tənzimlənir ki, stoper onun üçün növbəti elementə, o isə stoper üçün əvvəlki elementə çevrilsin. Növbə elementi sayğacı artırılır.

Növbədən elementin götürülməsi:

mblk_t * getq(queue_t *q); 

Stoperdən sonra gələn mesaj alınır və element sayğacı azalır. Əgər növbədə stoperdən başqa heç bir element yoxdursa, onda 0 qaytarılır.

Mesajın növbəyə daxil edilməsi:

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

Element mp elementdən əvvəl daxil edilir ƏMİP. Əgər ƏMİP=0, sonra mesaj növbənin quyruğuna əlavə olunur.

Növbənin rəhbərindən mesaj alınır:

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

Element sayğacı azalır.

Növbədəki birinci elementə göstərici oxunur:

mblk_t * peekq(queue_t *q); 

Elementlərin özlərini silərkən növbədən bütün elementlərin çıxarılması:

void flushq(queue_t *q, int how);

arqument necə istifadə olunmur. Növbə elementi sayğacı sıfıra təyin edilib.

Növbənin sonuncu elementinə göstərici oxumaq üçün makro:

mblk_t * qlast(queue_t *q);

Mesaj növbələri ilə işləyərkən, zəng etdiyiniz zaman xəbərdar olun ms_queue_put(q, m) mesaja null göstəricisi ilə funksiya dönər. Proqramınız donacaq. oxşar davranır ms_queue_next(q, m).

Filtrlərin birləşdirilməsi

Yuxarıda təsvir olunan növbə mesajları bir filtrdən digərinə və ya birindən bir neçə filtrə ötürmək üçün istifadə olunur. Süzgəclər və onların əlaqələri istiqamətləndirilmiş qrafik təşkil edir. Süzgəcin girişi və ya çıxışı ümumi “pin” sözü adlanacaq. Filtrlərin bir-birinə qoşulma qaydasını təsvir etmək üçün media yayımçısı “siqnal nöqtəsi” anlayışından istifadə edir. Siqnal nöqtəsi quruluşdur _MSCPPoint, filtrə bir göstərici və onun sancaqlarından birinin nömrəsini ehtiva edir; müvafiq olaraq, filtrin giriş və ya çıxışlarından birinin əlaqəsini təsvir edir.

Məlumatların işlənməsi qrafikinin siqnal nöqtəsi

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

Filtr sancaqları sıfırdan başlayaraq nömrələnir.

İki sancağın bir mesaj növbəsi ilə əlaqəsi struktur tərəfindən təsvir olunur _MSNövbə, mesaj növbəsini və birləşdirdiyi iki siqnal nöqtəsinə göstəriciləri ehtiva edir:

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

Biz bu quruluşu siqnal bağı adlandıracağıq. Hər bir media axın filtri giriş bağlantıları cədvəlini və çıxış bağlantıları cədvəlini ehtiva edir (MSQueue). Cədvəllərin ölçüsü filtr yaratarkən təyin olunur; biz bunu artıq ixrac edilmiş tip dəyişənindən istifadə edərək etdik MSFilterDesc, öz filtrimizi hazırladığımız zaman. Aşağıda media yayımlayıcısındakı hər hansı filtri təsvir edən bir quruluş var, 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;

Planımıza uyğun olaraq C proqramındakı filtrləri birləşdirdikdən sonra (lakin işarəni bağlamadıq), bununla da qovşaqları strukturun nümunələri olan istiqamətləndirilmiş bir qrafik yaratdıq. MSFilter, və kənarlar keçid nümunələridir MSQueue.

Tickerin pərdəarxası fəaliyyətləri

Mən sizə tickerin gənə mənbəyi üçün bir filtr olduğunu söylədikdə, bu, bu barədə bütün həqiqət deyildi. Ticker saatda funksiyaları yerinə yetirən bir obyektdir proses() qoşulduğu dövrənin (qrafik) bütün filtrləri. C proqramında qrafik filtrinə işarə qoşduqda biz onu söndürənə qədər onun bundan sonra idarə edəcəyi qrafiki göstəririk. Qoşulduqdan sonra ticker, ona daxil olan filtrlərin siyahısını tərtib edərək, ona həvalə edilmiş qrafiki araşdırmağa başlayır. Eyni filtri iki dəfə “saymamaq” üçün aşkar edilmiş filtrləri onların içinə işarə qoyaraq qeyd edir. görüldü. Axtarış hər bir filtrdə olan link cədvəllərindən istifadə etməklə həyata keçirilir.

Qrafikə giriş turu zamanı ticker filtrlər arasında məlumat bloklarının mənbəyi kimi çıxış edən ən azı birinin olub olmadığını yoxlayır. Əgər bunlar yoxdursa, o zaman qrafik səhv hesab olunur və ticker çökür.

Qrafik "düzgün" olarsa, tapılan hər bir filtr üçün funksiya işə salınmağa çağırılır preprocess(). Növbəti emal dövrü üçün an gələn kimi (standart olaraq hər 10 millisaniyədən bir) ticker funksiyanı çağırır. proses() əvvəllər tapılmış bütün mənbə filtrləri üçün, sonra isə siyahıda qalan filtrlər üçün. Filtrdə giriş bağlantıları varsa, funksiyanı işə salır proses() giriş keçid növbələri boş olana qədər təkrarlanır. Bundan sonra o, siyahıdakı növbəti filtrə keçir və daxiletmə bağlantıları mesajlardan azad olana qədər onu “sürüşdürür”. Siyahı bitənə qədər işarə filtrdən filtrə keçir. Bu, dövrün işlənməsini tamamlayır.

İndi biz tüllərə qayıdacağıq və niyə belə bir obyektin media striminə əlavə edildiyi barədə danışacağıq. Ümumiyyətlə, filtr daxilində işləyən alqoritmin tələb etdiyi məlumatların miqdarı üst-üstə düşmür və girişdə qəbul edilən məlumat buferlərinin ölçüsünün qatı deyil. Məsələn, biz sürətli Furye transformasiyasını həyata keçirən bir filtr yazırıq və bu, tərifinə görə yalnız ölçüsü ikinin gücü olan məlumat bloklarını emal edə bilər. 512 say olsun. Məlumat telefon kanalı tərəfindən yaradılırsa, girişdəki hər mesajın məlumat buferi bizə 160 siqnal nümunəsi gətirəcəkdir. Tələb olunan məlumat miqdarı olana qədər girişdən məlumat toplamamaq cazibədardır. Ancaq bu vəziyyətdə, giriş bağlantısı boş olana qədər filtri uğursuz şəkildə sürüşdürməyə çalışacaq ticker ilə toqquşma baş verəcəkdir. Əvvəllər bu qaydanı filtrin üçüncü prinsipi kimi təyin etdik. Bu prinsipə əsasən, filtrin proses() funksiyası giriş növbələrindən bütün məlumatları qəbul etməlidir.

Bundan əlavə, girişdən yalnız 512 nümunə götürmək mümkün olmayacaq, çünki yalnız bütün blokları götürə bilərsiniz, yəni. filtr 640 nümunə götürməli və onlardan 512-ni istifadə etməli, qalanı isə məlumatların yeni hissəsini toplamadan əvvəl. Beləliklə, filtrimiz əsas işinə əlavə olaraq, daxil edilmiş məlumatların aralıq saxlanması üçün köməkçi hərəkətləri təmin etməlidir. Media striminin tərtibatçıları və bu ümumi problemin həlli xüsusi bir obyekt - MSBufferizer (bufer) işləyib hazırlayıblar ki, bu problem dəstlərdən istifadə edərək həll edir.

Buferizator (MSBufferizer)

Bu, filtrin daxilində giriş məlumatlarını toplayacaq və məlumatın miqdarı filtr alqoritmini işə salmaq üçün kifayət edən kimi onu emal üçün təqdim etməyə başlayacaq obyektdir. Bufer məlumat yığarkən, filtr prosessorun emal gücündən istifadə etmədən boş rejimdə işləyəcək. Lakin buferdən oxu funksiyası sıfırdan fərqli bir dəyər qaytaran kimi, filtrin proses() funksiyası buferdən məlumatları tələb olunan ölçüdə hissələrlə, tükənənə qədər götürməyə və emal etməyə başlayır.
Hələ tələb olunmayan məlumatlar buferdə giriş məlumatlarının sonrakı bloklarının əlavə olunduğu çuxurun ilk elementi kimi qalır.

Buferi təsvir edən struktur:

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

MSBufferizer ilə işləmək üçün funksiyalar

Yeni bufer nümunəsinin yaradılması:

MSBufferizer * ms_bufferizer_new(void);

Yaddaş ayrılır, işə salınır ms_bufferizer_init() və göstərici qaytarılır.

Başlama funksiyası:

void ms_bufferizer_init(MSBufferizer *obj); 

Növbə işə salınır q, sahə boy sıfıra təyin edilir.

Mesaj əlavə edilir:

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

Növbəyə m mesajı əlavə olunur. Məlumat bloklarının hesablanmış ölçüsü əlavə olunur boy.

Bütün mesajların keçid verilənləri növbəsindən buferə köçürülməsi q:

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

Linkdən mesajların ötürülməsi q buferdə funksiyasından istifadə etməklə yerinə yetirilir ms_bufferizer_put().

Buferdən oxumaq:

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

Buferdə toplanmış məlumatların ölçüsü tələb olunandan azdırsa (datalen), onda funksiya sıfırı qaytarır, verilənlər verilənlərə kopyalanmır. Əks halda, buferdə yerləşən dəstlərdən verilənlərin ardıcıl surəti həyata keçirilir. Kopyalamadan sonra dəst silinir və yaddaş boşaldılır. Kopyalama datalen baytların kopyalandığı anda başa çatır. Əgər məlumat blokunun ortasında yer tükənirsə, bu mesajda məlumat bloku qalan kopyalanmamış hissəyə qədər azalacaq. Növbəti dəfə zəng etdiyiniz zaman kopyalama bu nöqtədən davam edəcək.

Buferdə hazırda mövcud olan məlumat miqdarının oxunması:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Sahəni qaytarır boy bufer.

Buferdəki məlumatların bir hissəsinin silinməsi:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Müəyyən edilmiş bayt sayı götürülür və silinir. Ən köhnə məlumatlar silinir.

Buferdəki bütün mesajların silinməsi:

void ms_bufferizer_flush(MSBufferizer *obj); 

Məlumat sayğacı sıfıra sıfırlanır.

Buferdəki bütün mesajların silinməsi:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Sayğac sıfırlanmayıb.

Buferin çıxarılması və yaddaşın boşaldılması:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Buferdən istifadə nümunələri bir neçə media axını filtrinin mənbə kodunda tapıla bilər. Məsələn, MS_L16_ENC filtrində, nümunələrdəki baytları şəbəkə sifarişindən host sırasına qədər yenidən təşkil edir: l16.c

Növbəti məqalədə biz ticker yükünün qiymətləndirilməsi məsələsinə və media streamerdə həddindən artıq hesablama yükü ilə necə məşğul olacağımıza baxacağıq.

Mənbə: www.habr.com

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