Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm

Makalenin materyali benim zenci kanalı.

Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm

Veri taşıma mekanizması

  • Veri bloğu dblk_t
  • Mesaj mblk_t
  • Mblk_t mesajlarıyla çalışma işlevleri
  • Kuyruk kuyruğu_t
  • Kuyruklarla çalışmaya yönelik işlevlerquee_t
  • Filtreleri bağlama
  • Veri işleme grafiğinin sinyal noktası
  • Ticker'ın sahne arkası etkinlikleri
  • Tamponlayıcı (MSBufferizer)
  • MSBufferizer ile çalışmaya yönelik işlevler

Geçmişte Makale kendi filtremizi geliştirdik. Bu makale, verileri medya aktarıcı filtreleri arasında taşımak için kullanılan dahili mekanizmaya odaklanacaktır. Bu, gelecekte daha az çabayla karmaşık filtreler yazmanıza olanak sağlayacaktır.

Veri taşıma mekanizması

Medya aktarıcıdaki veri hareketi, yapı tarafından tanımlanan kuyruklar kullanılarak gerçekleştirilir kuyruk_t. Gibi mesaj dizileri mlk_t, kendileri sinyal verisi içermez, ancak yalnızca önceki, sonraki mesaja ve veri bloğuna bağlantı verir. Ayrıca, aynı türdeki bir mesaja bağlantı için tek bir bağlantılı mesaj listesi düzenlemenize olanak tanıyan bir alanın da bulunduğunu özellikle vurgulamak isterim. Böyle bir listeyle birleştirilen bir grup mesaja tuple adını vereceğiz. Böylece kuyruğun herhangi bir elemanı tek bir mesaj olabilir. mlk_tve belki bir mesaj grubunun başı mlk_t. Her tuple mesajının kendi koğuş veri bloğu olabilir. Tuple'lara neden ihtiyaç duyulduğunu biraz sonra tartışacağız.

Yukarıda belirtildiği gibi mesajın kendisi bir veri bloğu içermez; bunun yerine yalnızca bloğun saklandığı hafıza alanına bir işaretçi içerir. Bu bölümde, medya aktarıcısının çalışmasının genel resmi, kapıların (veri bağlantıları - odalar) havai konveyörler boyunca inanılmaz bir hızla hareket ettiği ve odaların kendilerinin olduğu “Monsters, Inc.” adlı çizgi filmdeki kapı deposunu anımsatıyor. hareketsiz kalmak.

Şimdi hiyerarşi boyunca aşağıdan yukarıya doğru ilerleyerek, medya aktarıcıdaki veri aktarım mekanizmasının listelenen varlıklarını ayrıntılı olarak ele alalım.

Veri bloğu dblk_t

Veri bloğu bir başlık ve bir veri arabelleğinden oluşur. Başlık aşağıdaki yapıyla tanımlanır:

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

Yapının alanları arabelleğin başlangıcına, arabelleğin sonuna ve veri arabelleğini silme işlevine işaret eden işaretçiler içerir. Başlıktaki son öğe db_ref — referans sayacı, eğer sıfıra ulaşırsa, bu, bu bloğun bellekten silinmesi için bir sinyal görevi görür. Veri bloğu işlev tarafından oluşturulmuşsa datab_alloc() , daha sonra veri arabelleği, başlıktan hemen sonra belleğe yerleştirilecektir. Diğer tüm durumlarda arabellek ayrı bir yere yerleştirilebilir. Veri arabelleği, filtrelerle işlemek istediğimiz sinyal örneklerini veya diğer verileri içerecektir.

Aşağıdaki işlev kullanılarak bir veri bloğunun yeni bir örneği oluşturulur:

dblk_t *datab_alloc(int size);

Giriş parametresi olarak bloğun depolayacağı verinin boyutu verilir. Tahsis edilen hafızanın başına bir başlık yapısı yerleştirmek için daha fazla hafıza tahsis edilir veri tabanı. Ancak diğer işlevleri kullanırken bu her zaman gerçekleşmez; bazı durumlarda veri arabelleği, veri bloğu başlığından ayrı olarak yerleştirilebilir. Bir yapı oluştururken alanlar, alanı öyle yapılandırılır ki db_base veri alanının başlangıcını işaret etti ve db_lim sonuna kadar. Bağlantı sayısı db_ref bir olarak ayarlanır. Veri temizleme işlevi işaretçisi sıfıra ayarlanır.

mesaj mlk_t

Belirtildiği gibi kuyruk elemanları şu türdendir: mblk_t, şu şekilde tanımlanır:

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;

Yapı mlk_t başında işaretçiler içerir b_önceki, b_sonrakiçift ​​bağlantılı bir liste düzenlemek için gerekli olan (bu bir kuyruktur) kuyruk_t).

Sonra işaretçi geliyor b_cont, yalnızca mesaj bir Tuple'ın parçası olduğunda kullanılır. Tuple'daki son mesaj için bu işaretçi boş kalır.

Daha sonra bir veri bloğuna işaret eden bir işaretçi görüyoruz b_datap, mesajın mevcut olduğu yer. Bunu, blok veri arabelleğinin içindeki alana işaret eden işaretçiler takip eder. Alan b_rptr ara bellekteki verilerin okunacağı konumu belirtir. Alan b_wptr ara belleğe yazmanın gerçekleştirileceği konumu belirtir.

Geri kalan alanlar hizmet niteliğindedir ve veri aktarım mekanizmasının işleyişiyle ilgili değildir.

Aşağıda adı taşıyan tek bir mesaj bulunmaktadır. m1 ve veri bloğu d1.
Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm
Aşağıdaki şekilde üç mesajdan oluşan bir grup gösterilmektedir m1, m1_1, m1_2.
Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm

Mesajlaşma işlevleri mlk_t

yeni bir mesaj mlk_t fonksiyon tarafından oluşturuldu:

mblk_t *allocb(int size, int pri); 

hafızasına yeni bir mesaj yerleştirir mlk_t belirtilen boyutta bir veri bloğu ile boyut, ikinci argüman - parçalar kitaplığın bu sürümünde kullanılmamaktadır. Sıfır kalmalı. Fonksiyonun çalışması sırasında yeni mesajın yapısına hafıza ayrılacak ve fonksiyon çağrılacaktır. mblk_init()yapının oluşturulan örneğinin tüm alanlarını sıfırlayacak ve ardından yukarıda belirtilenleri kullanarak datab_alloc(), bir veri arabelleği oluşturacaktır. Bundan sonra yapıdaki alanlar yapılandırılacaktır:

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

Çıkışta, başlatılan alanları ve boş bir veri arabelleğini içeren yeni bir mesaj alıyoruz. Bir mesaja veri eklemek için onu veri bloğu arabelleğine kopyalamanız gerekir:

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

nerede veri veri kaynağına yönelik bir işaretçidir ve boyut - boyutları.
daha sonra işaretçiyi, arabellekteki boş alanın başlangıcını tekrar işaret edecek şekilde yazma noktasına güncellemeniz gerekir:

msg->b_wptr = msg->b_wptr + size

Mevcut bir arabellekten kopyalamadan bir mesaj oluşturmanız gerekiyorsa, işlevi kullanın:

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

İşlev, mesajı ve veri bloğunun yapısını oluşturduktan sonra, işaretçilerini adresteki verilere göre yapılandıracaktır. buf. Onlar. bu durumda veri arabelleği, fonksiyonla bir veri bloğu oluştururken olduğu gibi veri bloğunun başlık alanlarından sonra bulunmaz. datab_alloc(). İşleve iletilen verileri içeren arabellek olduğu yerde kalacaktır, ancak işaretçilerin yardımıyla veri bloğunun yeni oluşturulan başlığına ve buna göre mesaja eklenecektir.

Bir mesaja mlk_t Birkaç veri bloğu sırayla birleştirilebilir. Bu, fonksiyon tarafından yapılır:

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

mp — başka bir veri bloğunun ekleneceği bir mesaj;
veri - bir kopyası mesaja eklenecek bloğun işaretçisi;
boyut — veri boyutu;
ped — tahsis edilen belleğin boyutunun 4 baytlık bir sınır boyunca hizalanması gerektiğini belirten bir işaret (doldurma sıfırlarla yapılacaktır).

Mevcut mesaj veri arabelleğinde yeterli alan varsa, yeni veriler zaten orada bulunan verilerin arkasına yapıştırılacaktır. Mesaj veri arabelleğinde olduğundan daha az boş alan varsa boyut, ardından yeterli arabellek boyutuna sahip yeni bir mesaj oluşturulur ve veriler onun arabelleğine kopyalanır. Bu, bir işaretçi kullanılarak orijinal mesaja bağlanan yeni bir mesajdır b_cont. Bu durumda mesaj bir Tuple'a dönüşür.

Tuple'a başka bir veri bloğu eklemeniz gerekiyorsa, işlevi kullanmanız gerekir:

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

Tuple'daki son mesajı bulacaktır (sahip olduğu b_cont null olacak) ve bu mesaja ilişkin işlevi çağıracak ek().

Aşağıdaki işlevi kullanarak bir mesajdaki veya demetteki verinin boyutunu öğrenebilirsiniz:

int msgdsize(const mblk_t *mp);

Tuple'daki tüm mesajlar arasında döngü yapacak ve bu mesajların veri arabelleklerindeki toplam veri miktarını döndürecektir. Her mesaj için veri miktarı şu şekilde hesaplanır:

 mp->b_wptr - mp->b_rptr

İki tuple'ı birleştirmek için işlevi kullanın:

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

tuple'ı ekliyor yeni tuple'ın kuyruğuna mp ve ortaya çıkan tuple'ın son mesajına bir işaretçi döndürür.

Gerekirse, bir demet tek bir veri bloğuyla tek bir mesaja dönüştürülebilir; bu, aşağıdaki fonksiyonla yapılır:

void msgpullup(mblk_t *mp,int len);

eğer argüman len -1 ise tahsis edilen arabelleğin boyutu otomatik olarak belirlenir. Eğer len Pozitif bir sayı ise bu boyutta bir arabellek oluşturulacak ve tuple mesaj verileri buraya kopyalanacaktır. Arabellek biterse kopyalama burada durur. Tuple'ın ilk mesajı, kopyalanan verilerle birlikte yeni boyutta bir arabellek alacaktır. Kalan mesajlar silinecek ve hafıza yığına geri dönecektir.

Bir yapıyı silerken mlk_t çağrı sırasında veri bloğunun referans sayısı dikkate alınır bedavab() sıfır olduğu ortaya çıkıyor, ardından veri arabelleği örnekle birlikte siliniyor mlk_t, bu da ona işaret ediyor.

Yeni bir mesajın alanlarının başlatılması:

void mblk_init(mblk_t *mp);

Mesaja başka bir veri parçası eklemek:

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

Yeni veriler mesaj veri arabelleğinin boş alanına uymuyorsa, gerekli boyutta bir arabelleğe sahip ayrı olarak oluşturulan bir mesaj mesaja eklenir (eklenen mesaja yönelik bir işaretçi ilk mesajda ayarlanır) ve mesaj bir tuple'a dönüşür.

Tuple'a bir parça veri ekleme:

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

İşlev bir döngüde Appendb() işlevini çağırır.

İki tuple'ı bir tanede birleştirmek:

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

mesaj yeni eklenecek mp.

Tek bir mesajın kopyasını oluşturmak:

mblk_t *copyb(const mblk_t *mp);

Bir demetin tüm veri bloklarıyla birlikte kopyalanmasını tamamlayın:

mblk_t *copymsg(const mblk_t *mp);

Tuple'ın elemanları fonksiyon tarafından kopyalanır kopyab().

Bir mesajın kolay bir kopyasını oluşturun mlk_t. Bu durumda veri bloğu kopyalanmaz ancak referans sayacı artırılır. db_ref:

mblk_t *dupb(mblk_t *mp);

Bir demetin hafif bir kopyasını oluşturma. Veri blokları kopyalanmaz, yalnızca referans sayaçları artırılır db_ref:

mblk_t *dupmsg(mblk_t* m);

Bir Tuple'ın tüm mesajlarını tek bir mesaja yapıştırmak:

void msgpullup(mblk_t *mp,size_t len);

Eğer argüman len -1 ise tahsis edilen arabelleğin boyutu otomatik olarak belirlenir.

Bir mesajı silme, tuple:

void freemsg(mblk_t *mp);

Veri bloğunun referans sayısı bir azaltılır. Sıfıra ulaşması durumunda veri bloğu da silinir.

Bir mesaj veya demetteki toplam veri miktarının hesaplanması.

size_t msgdsize(const mblk_t *mp);

Kuyruğun kuyruğundan bir mesajın alınması:

mblk_t *ms_queue_peek_last (q);

Bir mesajın ayrılmış alanlarının içeriğini başka bir mesaja kopyalamak (aslında bu alanlar medya aktarıcısı tarafından kullanılan bayrakları içerir):

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

Kuyruk kuyruk_t

Medya aktarıcısındaki mesaj kuyruğu, dairesel, çift bağlantılı bir liste olarak uygulanır. Her liste öğesi, sinyal örnekleri içeren bir veri bloğuna yönelik bir işaretçi içerir. Verilerin kendisi hareketsiz kalırken, yalnızca veri bloğuna işaret eden işaretçilerin sırayla hareket ettiği ortaya çıktı. Onlar. yalnızca onlara olan bağlantılar taşınır.
Kuyruğu açıklayan yapı kuyruk_t, aşağıda gösterilen:

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

Yapı bir alan içerir - bir işaretçi _q_stopper *mblk_t yazın, kuyruktaki ilk öğeyi (mesajı) gösterir. Yapının ikinci alanı kuyruktaki mesajların sayacıdır.
Aşağıdaki şekilde m1, m4, m1, m2 olmak üzere 3 mesaj içeren q4 isimli bir kuyruk gösterilmektedir.
Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm
Aşağıdaki şekilde m1,m4,m1,m2 olmak üzere 3 mesaj içeren q4 isimli bir kuyruk gösterilmektedir. Mesaj m2, m2_1 ve m2_2 olmak üzere iki mesaj daha içeren bir tuple'ın başıdır.

Mediastreamer2 VoIP motorunu keşfetme. 11. Bölüm

Kuyruklarla çalışmaya yönelik işlevlerquee_t

Kuyruk başlatma:

void qinit(queue_t *q);

Tarla _q_stopper (bundan sonra buna "durdurucu" diyeceğiz) fonksiyon tarafından başlatılır mblk_init(), önceki öğesi ve sonraki öğe işaretçisi kendisini gösterecek şekilde ayarlanır. Kuyruk öğesi sayacı sıfıra sıfırlanır.

Yeni bir öğe ekleme (mesajlar):

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

Yeni öğe m Listenin sonuna eklenirse, öğe işaretçileri, durdurucunun bir sonraki öğe olacağı ve durdurucunun bir önceki öğesi olacağı şekilde ayarlanır. Kuyruk öğesi sayacı artırılır.

Kuyruktan bir öğenin alınması:

mblk_t * getq(queue_t *q); 

Durdurucu alındıktan sonra çıkan mesaj ve eleman sayacı azaltılır. Kuyrukta durdurucu dışında hiçbir öğe yoksa 0 değeri döndürülür.

Kuyruğa bir mesaj ekleme:

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

eleman mp elemanın önüne eklendi emp. Eğer emp=0 ise mesaj kuyruğun kuyruğuna eklenir.

Kuyruğun başındaki kişiden mesaj alma:

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

Eleman sayacı azaltılır.

Kuyruktaki ilk öğeye yönelik bir işaretçiyi okuma:

mblk_t * peekq(queue_t *q); 

Öğelerin kendileri silinirken tüm öğelerin kuyruktan kaldırılması:

void flushq(queue_t *q, int how);

tartışma Nasıl kullanılmamış. Kuyruk öğesi sayacı sıfıra ayarlandı.

Kuyruğun son öğesine işaretçiyi okumak için makro:

mblk_t * qlast(queue_t *q);

Mesaj kuyruklarıyla çalışırken, aradığınızda ms_queue_put(q, m) mesaja boş bir işaretçi ile işlev döngüye girer. Programınız donacaktır. benzer şekilde davranır ms_queue_next(q, m).

Filtreleri bağlama

Yukarıda açıklanan kuyruk, mesajları bir filtreden diğerine veya bir filtreden birkaç filtreye geçirmek için kullanılır. Filtreler ve bağlantıları yönlendirilmiş bir grafik oluşturur. Filtrenin giriş veya çıkışına genel "pin" kelimesi adı verilecektir. Medya aktarıcı, filtrelerin birbirine bağlanma sırasını tanımlamak için "sinyal noktası" kavramını kullanır. Sinyal noktası yapıdır _MSCPuantıFiltreye bir işaretçi ve pinlerinden birinin numarasını içeren, buna göre filtrenin giriş veya çıkışlarından birinin bağlantısını açıklar.

Veri işleme grafiğinin sinyal noktası

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

Filtre pinleri sıfırdan başlayarak numaralandırılır.

İki pinin bir mesaj kuyruğuna bağlanması yapı ile açıklanmaktadır. _MSQueueBir mesaj kuyruğunu ve bağladığı iki sinyal noktasına işaretçileri içeren:

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

Bu yapıya sinyal bağlantısı adını vereceğiz. Her medya aktarıcı filtresi, bir giriş bağlantıları tablosu ve bir çıkış bağlantıları tablosu içerir (MSQueue). Tabloların boyutu filtre oluşturulurken ayarlanır; bunu zaten dışa aktarılan bir tür değişkeni kullanarak yapmıştık MSFilterDesc, kendi filtremizi geliştirdiğimizde. Aşağıda bir medya aktarıcıdaki herhangi bir filtreyi açıklayan bir yapı bulunmaktadır. MSFiltre:


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 programındaki filtreleri planımıza uygun olarak bağladıktan sonra (fakat işaretlemeyi bağlamadan), düğümleri yapının örnekleri olan yönlendirilmiş bir grafik oluşturduk. MSFiltreve kenarlar bağlantıların örnekleridir MSQueue.

Ticker'ın sahne arkası etkinlikleri

Titreşimin, tiklerin kaynağına yönelik bir filtre olduğunu söylediğimde, bu konudaki tüm gerçek bu değildi. Ticker, saatte işlevleri çalıştıran bir nesnedir işlem() bağlı olduğu devrenin (grafiğin) tüm filtreleri. Bir C programındaki bir grafik filtresine bir Ticker bağladığımızda, Ticker'a bundan sonra biz onu kapatana kadar kontrol edeceği grafiği gösteririz. Bağlandıktan sonra, onaylayıcı kendisine emanet edilen grafiği incelemeye başlar ve onu içeren filtrelerin bir listesini derler. Aynı filtreyi iki kez “saymamak” için, tespit edilen filtreleri içlerine onay kutusu koyarak işaretler. görüldü. Arama, her filtrenin sahip olduğu bağlantı tabloları kullanılarak gerçekleştirilir.

Grafiğin giriş turu sırasında, kayan yazı, filtreler arasında veri bloklarının kaynağı olarak görev yapan en az bir tane olup olmadığını kontrol eder. Hiçbiri yoksa grafik hatalı kabul edilir ve kayan yazı çöker.

Grafiğin “doğru” olduğu ortaya çıkarsa, bulunan her filtre için işlev, başlatma için çağrılır. önişleme(). Bir sonraki işlem döngüsü için an geldiğinde (varsayılan olarak her 10 milisaniyede bir), kayan yazı işlevi çağırır işlem() daha önce bulunan tüm kaynak filtreler ve ardından listede kalan filtreler için. Filtrenin giriş bağlantıları varsa işlevi çalıştırmak işlem() giriş bağlantısı kuyrukları boşalana kadar tekrarlanır. Bundan sonra listedeki bir sonraki filtreye geçer ve giriş bağlantılarında mesaj kalmayıncaya kadar onu "kaydırır". Liste bitene kadar şerit filtreden filtreye hareket eder. Bu, döngünün işlenmesini tamamlar.

Şimdi tuple'lara döneceğiz ve böyle bir varlığın medya aktarıcıya neden eklendiğini konuşacağız. Genel olarak, filtrenin içinde çalışan algoritmanın ihtiyaç duyduğu veri miktarı, girişte alınan veri arabelleklerinin boyutunun katı değildir ve çakışmaz. Örneğin, hızlı Fourier dönüşümü gerçekleştiren ve tanımı gereği yalnızca boyutu ikinin katı olan veri bloklarını işleyebilen bir filtre yazıyoruz. 512 sayım olsun. Veriler bir telefon kanalı tarafından üretiliyorsa, girişteki her mesajın veri arabelleği bize 160 sinyal örneği getirecektir. Gerekli miktarda veri elde edilene kadar girdiden veri toplamamak cazip gelebilir. Ancak bu durumda, kayan yazı ile bir çarpışma meydana gelecek ve bu, giriş bağlantısı boşalana kadar filtreyi kaydırmayı başarısız bir şekilde deneyecektir. Daha önce bu kuralı filtrenin üçüncü ilkesi olarak belirtmiştik. Bu prensibe göre, filtrenin proses() fonksiyonunun giriş kuyruğundaki tüm verileri alması gerekir.

Ayrıca girişten sadece 512 örnek almak mümkün olmayacaktır çünkü sadece tam blokları yani. filtrenin 640 örnek alması ve bunlardan 512'sini kullanması gerekecek, geri kalanını ise yeni bir veri bölümü toplamadan önce kullanacaktır. Bu nedenle filtremiz, ana işine ek olarak, giriş verilerinin ara depolanması için yardımcı eylemler sağlamalıdır. Medya aktarıcının geliştiricileri ve bu genel soruna çözüm, bu sorunu tuple'lar kullanarak çözen özel bir nesne olan MSBufferizer (tamponlayıcı) geliştirdiler.

Tamponlayıcı (MSBufferizer)

Bu, giriş verilerini filtre içinde biriktirecek ve bilgi miktarı filtre algoritmasını çalıştırmak için yeterli olduğunda bunu işlenmek üzere göndermeye başlayacak bir nesnedir. Arabellek veri biriktirirken filtre, işlemcinin işlem gücünü tüketmeden boş modda çalışacaktır. Ancak arabellekten okuma işlevi sıfırdan farklı bir değer döndürdüğü anda, filtrenin proses() işlevi, arabellekten gerekli boyuttaki kısımlar halinde veriyi tükenene kadar almaya ve işlemeye başlar.
Henüz gerekli olmayan veriler, sonraki giriş verileri bloklarının eklendiği demetin ilk öğesi olarak arabellekte kalır.

Tamponu tanımlayan yapı:

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

MSBufferizer ile çalışmaya yönelik işlevler

Yeni bir arabellek örneği oluşturma:

MSBufferizer * ms_bufferizer_new(void);

Bellek tahsis edildi, başlatıldı ms_bufferizer_init() ve bir işaretçi döndürülür.

Başlatma işlevi:

void ms_bufferizer_init(MSBufferizer *obj); 

Kuyruk başlatılıyor q, alan boyut sıfıra ayarlanmıştır.

Mesaj ekleme:

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

m mesajı kuyruğa eklenir. Hesaplanan veri bloklarının boyutu şuna eklenir: boyut.

Tüm mesajları bağlantı veri kuyruğundan ara belleğe aktarma q:

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

Bir bağlantıdan mesaj aktarma q tamponda fonksiyon kullanılarak gerçekleştirilir ms_bufferizer_put().

Tampondan okuma:

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

Tamponda biriken verinin boyutu istenenden küçükse (verilen), bu durumda işlev sıfır döndürür, veriler verilere kopyalanmaz. Aksi takdirde, arabellekte bulunan tuple'lardan verilerin sıralı kopyalanması gerçekleştirilir. Kopyalamanın ardından tuple silinir ve hafıza serbest bırakılır. Kopyalama, veri baytlarının kopyalandığı anda sona erer. Bir veri bloğunun ortasında yer biterse, bu mesajda veri bloğu kopyalanmamış kalan kısma indirgenecektir. Bir sonraki aramanızda kopyalama bu noktadan devam edecektir.

Şu anda arabellekte mevcut olan veri miktarının okunması:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Alanı döndürür boyut tamponlayıcı.

Arabellekteki verilerin bir kısmının atılması:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Belirtilen sayıda bayt veri alınır ve atılır. En eski veriler atılır.

Tampondaki tüm mesajların silinmesi:

void ms_bufferizer_flush(MSBufferizer *obj); 

Veri sayacı sıfırlanır.

Tampondaki tüm mesajların silinmesi:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Sayaç sıfırlanmaz.

Tamponun çıkarılması ve belleğin serbest bırakılması:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Tamponlayıcının kullanımına ilişkin örnekler, çeşitli medya aktarıcı filtrelerinin kaynak kodunda bulunabilir. Örneğin, örneklerdeki baytları ağ sırasından ana bilgisayar sırasına göre yeniden düzenleyen MS_L16_ENC filtresinde: l16.c

Bir sonraki makalede, bir kayan yazı üzerindeki yükü tahmin etme konusuna ve bir medya aktarıcıdaki aşırı bilgi işlem yüküyle mücadele etmenin yollarına bakacağız.

Kaynak: habr.com

Yorum ekle