Meneroka enjin Mediastreamer2 VoIP. Bahagian 11

Bahan artikel diambil dari saya saluran zen.

Meneroka enjin Mediastreamer2 VoIP. Bahagian 11

Mekanisme pergerakan data

  • Blok data dblk_t
  • Mesej mblk_t
  • Fungsi untuk bekerja dengan mesej mblk_t
  • Baris gilir_t
  • Fungsi untuk bekerja dengan queue_t
  • Menyambung penapis
  • Titik isyarat graf pemprosesan data
  • Aktiviti di sebalik tabir
  • Penampan (MSBufferizer)
  • Fungsi untuk bekerja dengan MSBufferizer

Pada masa lalu artikel kami telah membangunkan penapis kami sendiri. Artikel ini akan menumpukan pada mekanisme dalaman untuk mengalihkan data antara penapis penstrim media. Ini akan membolehkan anda menulis penapis yang canggih dengan usaha yang kurang pada masa hadapan.

Mekanisme pergerakan data

Pergerakan data dalam penstrim media dilakukan menggunakan baris gilir yang diterangkan oleh struktur queue_t. Rentetan mesej seperti mblk_t, yang sendiri tidak mengandungi data isyarat, tetapi hanya memaut ke mesej sebelumnya, seterusnya dan ke blok data. Di samping itu, saya ingin menekankan terutamanya bahawa terdapat juga medan untuk pautan ke mesej jenis yang sama, yang membolehkan anda mengatur senarai mesej yang dipautkan secara tunggal. Kami akan memanggil sekumpulan mesej yang disatukan oleh senarai sedemikian sebagai tuple. Oleh itu, mana-mana elemen baris gilir boleh menjadi satu mesej mblk_t, dan mungkin ketua tuple mesej mblk_t. Setiap mesej tuple boleh mempunyai blok data wad sendiri. Kami akan membincangkan mengapa tupel diperlukan sedikit kemudian.

Seperti yang dinyatakan di atas, mesej itu sendiri tidak mengandungi blok data; sebaliknya, ia hanya mengandungi penunjuk ke kawasan memori di mana blok itu disimpan. Dalam bahagian ini, gambaran keseluruhan kerja penstrim media mengingatkan gudang pintu dalam kartun "Monsters, Inc.," di mana pintu (pautan ke data - bilik) bergerak pada kelajuan yang gila di sepanjang penghantar atas, manakala bilik itu sendiri kekal tidak bergerak.

Sekarang, bergerak mengikut hierarki dari bawah ke atas, mari kita pertimbangkan secara terperinci entiti tersenarai bagi mekanisme penghantaran data dalam penstrim media.

Blok data dblk_t

Blok data terdiri daripada pengepala dan penimbal data. Pengepala diterangkan oleh struktur berikut,

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

Medan struktur mengandungi penunjuk ke permulaan penimbal, penghujung penimbal dan fungsi untuk memadam penimbal data. Elemen terakhir dalam pengepala db_ref — kaunter rujukan, jika ia mencapai sifar, ini berfungsi sebagai isyarat untuk memadam blok ini daripada ingatan. Jika blok data dicipta oleh fungsi datab_alloc() , maka penimbal data akan diletakkan dalam ingatan sejurus selepas pengepala. Dalam semua kes lain, penimbal boleh ditempatkan di suatu tempat secara berasingan. Penampan data akan mengandungi sampel isyarat atau data lain yang ingin kami proses dengan penapis.

Contoh baharu bagi blok data dibuat menggunakan fungsi:

dblk_t *datab_alloc(int size);

Sebagai parameter input, ia diberikan saiz data yang akan disimpan oleh blok. Lebih banyak memori diperuntukkan untuk meletakkan tajuk - struktur - pada permulaan memori yang diperuntukkan datab. Tetapi apabila menggunakan fungsi lain, ini tidak selalu berlaku; dalam beberapa kes, penimbal data mungkin terletak secara berasingan daripada pengepala blok data. Apabila mencipta struktur, medan dikonfigurasikan supaya medannya db_base menunjuk ke permulaan kawasan data, dan db_lim ke penghujungnya. Kiraan pautan db_ref ditetapkan kepada satu. Penunjuk fungsi jelas data ditetapkan kepada sifar.

mesej mblk_t

Seperti yang dinyatakan, elemen baris gilir adalah jenis mblk_t, ia ditakrifkan seperti berikut:

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 mengandungi petunjuk pada permulaan b_prev, b_seterusnya, yang diperlukan untuk mengatur senarai terpaut berganda (iaitu baris gilir queue_t).

Kemudian datang penunjuk b_samb, yang hanya digunakan apabila mesej adalah sebahagian daripada tupel. Untuk mesej terakhir dalam tupel, penunjuk ini kekal batal.

Seterusnya kita melihat penunjuk ke blok data b_datap, yang mana mesej itu wujud. Ia diikuti dengan penunjuk ke kawasan di dalam penimbal data blok. Padang b_rptr menentukan lokasi dari mana data daripada penimbal akan dibaca. Padang b_wptr menunjukkan lokasi dari mana penulisan kepada penimbal akan dilakukan.

Medan selebihnya adalah bersifat perkhidmatan dan tidak berkaitan dengan operasi mekanisme pemindahan data.

Di bawah ialah satu mesej dengan nama m1 dan blok data d1.
Meneroka enjin Mediastreamer2 VoIP. Bahagian 11
Rajah berikut menunjukkan tuple tiga mesej m1, m1_1, m1_2.
Meneroka enjin Mediastreamer2 VoIP. Bahagian 11

Fungsi pemesejan mblk_t

Mesej baru mblk_t dicipta oleh fungsi:

mblk_t *allocb(int size, int pri); 

dia meletakkan mesej baru dalam ingatan mblk_t dengan blok data saiz yang ditentukan saiz, hujah kedua - pri tidak digunakan dalam versi perpustakaan ini. Ia sepatutnya kekal sifar. Semasa operasi fungsi, memori akan diperuntukkan untuk struktur mesej baharu dan fungsi akan dipanggil mblk_init(), yang akan menetapkan semula semua medan contoh struktur yang dibuat dan kemudian, menggunakan yang disebutkan di atas datab_alloc(), akan mencipta penimbal data. Selepas itu medan dalam struktur akan dikonfigurasikan:

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

Pada output kami menerima mesej baharu dengan medan yang dimulakan dan penimbal data kosong. Untuk menambah data pada mesej, anda perlu menyalinnya ke penimbal blok data:

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

mana data ialah penunjuk kepada sumber data, dan saiz - saiz mereka.
maka anda perlu mengemas kini penunjuk ke titik tulis supaya ia menunjuk semula ke permulaan kawasan bebas dalam penimbal:

msg->b_wptr = msg->b_wptr + size

Jika anda perlu membuat mesej daripada penimbal sedia ada, tanpa menyalin, kemudian gunakan fungsi:

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

Fungsi, selepas mencipta mesej dan struktur blok data, akan mengkonfigurasi penunjuknya kepada data di alamat penggemar. Itu. dalam kes ini, penimbal data tidak terletak selepas medan pengepala blok data, seperti yang berlaku semasa membuat blok data dengan fungsi datab_alloc(). Penampan dengan data yang dihantar ke fungsi akan kekal di mana ia berada, tetapi dengan bantuan penunjuk ia akan dilampirkan pada pengepala blok data yang baru dibuat, dan itu, sewajarnya, pada mesej.

Kepada satu mesej mblk_t Beberapa blok data boleh digabungkan secara berurutan. Ini dilakukan oleh fungsi:

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

mp — mesej yang mana blok data lain akan ditambah;
data — penunjuk ke blok, salinannya akan ditambahkan pada mesej;
saiz — saiz data;
pad — bendera yang saiz memori yang diperuntukkan mesti diselaraskan di sepanjang sempadan 4-bait (padding akan dilakukan dengan sifar).

Jika terdapat ruang yang mencukupi dalam penimbal data mesej sedia ada, maka data baharu akan ditampal di belakang data yang sudah ada. Jika terdapat kurang ruang kosong dalam penimbal data mesej daripada saiz, maka mesej baharu dibuat dengan saiz penimbal yang mencukupi dan data disalin ke penimbalnya. Ini ialah mesej baharu, dipautkan kepada mesej asal menggunakan penuding b_samb. Dalam kes ini, mesej bertukar menjadi tuple.

Jika anda perlu menambah satu lagi blok data ke tuple, maka anda perlu menggunakan fungsi:

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

dia akan mencari mesej terakhir dalam tupel (dia ada b_samb akan menjadi batal) dan akan memanggil fungsi untuk mesej ini appendb().

Anda boleh mengetahui saiz data dalam mesej atau tupel menggunakan fungsi:

int msgdsize(const mblk_t *mp);

ia akan mengulangi semua mesej dalam tuple dan mengembalikan jumlah data dalam penimbal data mesej tersebut. Untuk setiap mesej, jumlah data dikira seperti berikut:

 mp->b_wptr - mp->b_rptr

Untuk menggabungkan dua tupel, gunakan fungsi:

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

dia menambah tupel newm ke ekor tupel mp dan mengembalikan penunjuk kepada mesej terakhir tupel yang terhasil.

Jika perlu, tuple boleh diubah menjadi satu mesej dengan satu blok data; ini dilakukan oleh fungsi:

void msgpullup(mblk_t *mp,int len);

jika hujah len ialah -1, maka saiz penimbal yang diperuntukkan ditentukan secara automatik. Jika len ialah nombor positif, penimbal sebesar ini akan dibuat dan data mesej tuple akan disalin ke dalamnya. Jika penimbal kehabisan, penyalinan akan berhenti di situ. Mesej pertama tuple akan menerima penimbal saiz baharu dengan data yang disalin. Mesej yang selebihnya akan dipadamkan dan memori dikembalikan ke timbunan.

Apabila memadam struktur mblk_t kiraan rujukan blok data diambil kira jika, semasa membuat panggilan freeb() ternyata menjadi sifar, maka penimbal data dipadamkan bersama-sama dengan contoh mblk_t, yang menunjuk kepadanya.

Memulakan medan mesej baharu:

void mblk_init(mblk_t *mp);

Menambah sekeping data lain pada mesej:

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

Jika data baharu tidak sesuai dengan ruang kosong penimbal data mesej, maka mesej yang dibuat secara berasingan dengan penimbal saiz yang diperlukan dilampirkan pada mesej (penunjuk kepada mesej tambahan ditetapkan dalam mesej pertama) dan mesej bertukar menjadi tupel.

Menambah sekeping data pada tupel:

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

Fungsi ini memanggil appendb() dalam gelung.

Menggabungkan dua tupel menjadi satu:

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

mesej newm akan dilampirkan kepada mp.

Membuat salinan satu mesej:

mblk_t *copyb(const mblk_t *mp);

Penyalinan lengkap tuple dengan semua blok data:

mblk_t *copymsg(const mblk_t *mp);

Unsur-unsur tupel disalin oleh fungsi copyb().

Buat salinan mesej yang mudah mblk_t. Dalam kes ini, blok data tidak disalin, tetapi kaunter rujukannya ditingkatkan db_ref:

mblk_t *dupb(mblk_t *mp);

Membuat salinan tuple yang ringan. Blok data tidak disalin, hanya kaunter rujukannya ditambah db_ref:

mblk_t *dupmsg(mblk_t* m);

Melekatkan semua mesej tuple ke dalam satu mesej:

void msgpullup(mblk_t *mp,size_t len);

Jika hujah len ialah -1, maka saiz penimbal yang diperuntukkan ditentukan secara automatik.

Memadamkan mesej, tuple:

void freemsg(mblk_t *mp);

Kiraan rujukan blok data dikurangkan satu. Jika ia mencapai sifar, blok data juga dipadamkan.

Pengiraan jumlah data dalam mesej atau tupel.

size_t msgdsize(const mblk_t *mp);

Mendapatkan semula mesej dari ekor baris gilir:

mblk_t *ms_queue_peek_last (q);

Menyalin kandungan medan rizab satu mesej ke dalam mesej lain (sebenarnya, medan ini mengandungi bendera yang digunakan oleh penstrim media):

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

Beratur queue_t

Baris gilir mesej dalam strim media dilaksanakan sebagai senarai pautan berganda bulat. Setiap elemen senarai mengandungi penunjuk ke blok data dengan sampel isyarat. Ternyata hanya penunjuk ke blok data bergerak secara bergilir, manakala data itu sendiri kekal tidak bergerak. Itu. hanya pautan kepada mereka dialihkan.
Struktur yang menerangkan baris gilir queue_t, ditunjukkan di bawah:

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

Struktur mengandungi medan - penunjuk _q_penyumbat taip *mblk_t, ia menunjuk kepada elemen pertama (mesej) dalam baris gilir. Medan kedua struktur ialah pembilang mesej dalam baris gilir.
Rajah di bawah menunjukkan baris gilir bernama q1 yang mengandungi 4 mesej m1, m2, m3, m4.
Meneroka enjin Mediastreamer2 VoIP. Bahagian 11
Rajah berikut menunjukkan baris gilir bernama q1 yang mengandungi 4 mesej m1,m2,m3,m4. Mesej m2 ialah kepala tupel yang mengandungi dua lagi mesej m2_1 dan m2_2.

Meneroka enjin Mediastreamer2 VoIP. Bahagian 11

Fungsi untuk bekerja dengan queue_t

Permulaan baris gilir:

void qinit(queue_t *q);

Bidang _q_penyumbat (selepas ini kita akan memanggilnya "penyumbat") dimulakan oleh fungsi mblk_init(), elemen sebelumnya dan penunjuk elemen seterusnya dilaraskan untuk menunjuk ke dirinya sendiri. Kaunter elemen baris gilir ditetapkan semula kepada sifar.

Menambah elemen baharu (mesej):

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

Elemen baharu m ditambah pada penghujung senarai, penunjuk elemen dilaraskan supaya penyumbat menjadi elemen seterusnya untuknya, dan ia menjadi elemen sebelumnya untuk penyumbat. Kaunter elemen baris gilir dinaikkan.

Mendapatkan semula elemen dari baris gilir:

mblk_t * getq(queue_t *q); 

Mesej yang datang selepas penyumbat diambil, dan pembilang elemen dikurangkan. Jika tiada unsur dalam baris gilir kecuali penyumbat, maka 0 dikembalikan.

Memasukkan mesej ke dalam baris gilir:

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

Элемент mp dimasukkan sebelum elemen emp. Jika emp=0, maka mesej itu ditambahkan pada ekor baris gilir.

Mendapatkan semula mesej daripada ketua baris gilir:

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

Kaunter elemen dikurangkan.

Membaca penunjuk kepada elemen pertama dalam baris gilir:

mblk_t * peekq(queue_t *q); 

Mengalih keluar semua elemen dari baris gilir sambil memadamkan elemen itu sendiri:

void flushq(queue_t *q, int how);

hujah bagaimana tidak digunakan. Kaunter elemen baris gilir ditetapkan kepada sifar.

Makro untuk membaca penunjuk kepada elemen terakhir baris gilir:

mblk_t * qlast(queue_t *q);

Apabila bekerja dengan baris gilir mesej, ambil perhatian bahawa apabila anda memanggil ms_queue_put(q, m) dengan penuding nol kepada mesej, fungsi akan bergelung. Program anda akan membeku. berkelakuan serupa ms_queue_next(q, m).

Menyambung penapis

Barisan gilir yang diterangkan di atas digunakan untuk menghantar mesej dari satu penapis ke yang lain atau dari satu kepada beberapa penapis. Penapis dan sambungannya membentuk graf terarah. Input atau output penapis akan dipanggil perkataan umum "pin". Untuk menerangkan susunan penapis disambungkan antara satu sama lain, penstrim media menggunakan konsep "titik isyarat". Titik isyarat ialah struktur _MSCPoint, yang mengandungi penunjuk kepada penapis dan nombor salah satu pinnya; oleh itu, ia menerangkan sambungan salah satu input atau output penapis.

Titik isyarat graf pemprosesan data

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

Pin penapis dinomborkan bermula dari sifar.

Sambungan dua pin oleh baris gilir mesej diterangkan oleh struktur _MSQueue, yang mengandungi baris gilir mesej dan penunjuk kepada dua titik isyarat yang disambungkannya:

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

Kami akan memanggil struktur ini sebagai pautan isyarat. Setiap penapis strim media mengandungi jadual pautan input dan jadual pautan output (MSQueue). Saiz jadual ditetapkan semasa membuat penapis; kami telah melakukannya menggunakan pembolehubah jenis yang dieksport MSFilterDesc, apabila kami membangunkan penapis kami sendiri. Di bawah ialah struktur yang menerangkan sebarang penapis dalam penstrim media, 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;

Selepas kami menyambungkan penapis dalam program C mengikut rancangan kami (tetapi tidak menyambungkan ticker), kami dengan itu mencipta graf terarah, yang nodnya adalah contoh struktur MSFilter, dan tepi ialah contoh pautan MSQueue.

Aktiviti di sebalik tabir

Apabila saya memberitahu anda bahawa ticker adalah penapis untuk sumber kutu, ia bukanlah keseluruhan kebenaran mengenainya. Ticker ialah objek yang menjalankan fungsi pada jam proses() semua penapis litar (graf) yang disambungkan. Apabila kami menyambungkan penanda kepada penapis graf dalam program C, kami menunjukkan kepada penanda graf yang akan dikawal dari sekarang sehingga kami mematikannya. Selepas menyambung, penanda mula memeriksa graf yang diamanahkan kepada penjagaannya, menyusun senarai penapis yang menyertakannya. Untuk tidak "mengira" penapis yang sama dua kali, ia menandakan penapis yang dikesan dengan meletakkan kotak semak di dalamnya dilihat. Carian dijalankan menggunakan jadual pautan yang ada pada setiap penapis.

Semasa lawatan pengenalan grafnya, penanda menyemak sama ada antara penapis terdapat sekurang-kurangnya satu yang bertindak sebagai sumber blok data. Jika tiada, maka graf itu dianggap tidak betul dan penanda ranap.

Jika graf ternyata "betul", untuk setiap penapis yang ditemui, fungsi dipanggil untuk permulaan praproses(). Sebaik sahaja tiba masa untuk kitaran pemprosesan seterusnya (setiap 10 milisaat secara lalai), penanda memanggil fungsi proses() untuk semua penapis sumber yang ditemui sebelum ini, dan kemudian untuk penapis yang tinggal dalam senarai. Jika penapis mempunyai pautan input, kemudian jalankan fungsi tersebut proses() berulang sehingga baris gilir pautan input kosong. Selepas ini, ia beralih ke penapis seterusnya dalam senarai dan "menatalnya" sehingga pautan input bebas daripada mesej. Pendetik bergerak dari penapis ke penapis sehingga senarai tamat. Ini melengkapkan pemprosesan kitaran.

Sekarang kita akan kembali ke tupel dan bercakap tentang sebab entiti sedemikian telah ditambahkan pada strim media. Secara umum, jumlah data yang diperlukan oleh algoritma yang beroperasi di dalam penapis tidak bertepatan dan bukan gandaan saiz penimbal data yang diterima pada input. Sebagai contoh, kami sedang menulis penapis yang melakukan transformasi Fourier yang pantas, yang mengikut definisi hanya boleh memproses blok data yang saiznya ialah kuasa dua. Biarlah 512 kiraan. Jika data dijana oleh saluran telefon, maka penimbal data setiap mesej pada input akan membawa kita 160 sampel isyarat. Adalah menarik untuk tidak mengumpul data daripada input sehingga jumlah data yang diperlukan ada di sana. Tetapi dalam kes ini, perlanggaran akan berlaku dengan penanda, yang tidak berjaya cuba menatal penapis sehingga pautan input kosong. Sebelum ini, kami menetapkan peraturan ini sebagai prinsip ketiga penapis. Menurut prinsip ini, fungsi proses() penapis mesti mengambil semua data daripada baris gilir input.

Di samping itu, tidak mungkin untuk mengambil hanya 512 sampel daripada input, kerana anda hanya boleh mengambil keseluruhan blok, i.e. penapis perlu mengambil 640 sampel dan menggunakan 512 daripadanya, bakinya sebelum mengumpul bahagian data baharu. Oleh itu, penapis kami, sebagai tambahan kepada kerja utamanya, mesti menyediakan tindakan tambahan untuk penyimpanan perantaraan data input. Pemaju streamer media dan penyelesaian kepada masalah umum ini telah membangunkan objek khas - MSBufferizer (penampan), yang menyelesaikan masalah ini menggunakan tupel.

Penampan (MSBufferizer)

Ini ialah objek yang akan mengumpul data input di dalam penapis dan mula menyerahkannya untuk diproses sebaik sahaja jumlah maklumat mencukupi untuk menjalankan algoritma penapis. Semasa penimbal sedang mengumpul data, penapis akan beroperasi dalam mod melahu, tanpa menggunakan kuasa pemprosesan pemproses. Tetapi sebaik sahaja fungsi bacaan daripada penimbal mengembalikan nilai selain sifar, fungsi proses() penapis mula mengambil dan memproses data daripada penimbal dalam bahagian saiz yang diperlukan, sehingga ia habis.
Data yang belum diperlukan kekal dalam penimbal sebagai elemen pertama tuple, yang mana blok data input yang berikutnya dilampirkan.

Struktur yang menerangkan penimbal:

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

Fungsi untuk bekerja dengan MSBufferizer

Mencipta contoh penimbal baharu:

MSBufferizer * ms_bufferizer_new(void);

Memori diperuntukkan, dimulakan dalam ms_bufferizer_init() dan penunjuk dikembalikan.

Fungsi permulaan:

void ms_bufferizer_init(MSBufferizer *obj); 

Barisan sedang dimulakan q, padang saiz ditetapkan kepada sifar.

Menambah mesej:

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

Mesej m ditambahkan pada baris gilir. Saiz yang dikira blok data ditambah kepada saiz.

Memindahkan semua mesej daripada baris gilir data pautan ke penimbal q:

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

Memindahkan mesej daripada pautan q dalam penimbal dilakukan menggunakan fungsi ms_bufferizer_put().

Bacaan dari penimbal:

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

Jika saiz data terkumpul dalam penimbal adalah kurang daripada yang diminta (datalen), maka fungsi mengembalikan sifar, data tidak disalin ke data. Jika tidak, penyalinan berurutan data daripada tupel yang terletak dalam penimbal dilakukan. Selepas menyalin, tupel dipadamkan dan memori dibebaskan. Penyalinan tamat pada masa apabila bait datalen disalin. Jika ruang kehabisan di tengah-tengah blok data, maka dalam mesej ini, blok data akan dikurangkan kepada bahagian yang tidak disalin. Kali seterusnya anda membuat panggilan, penyalinan akan diteruskan dari titik ini.

Membaca jumlah data yang tersedia pada masa ini dalam penimbal:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Mengembalikan medan saiz penampan.

Membuang sebahagian daripada data dalam penimbal:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Bilangan bait data yang ditentukan diambil dan dibuang. Data tertua dibuang.

Memadam semua mesej dalam penimbal:

void ms_bufferizer_flush(MSBufferizer *obj); 

Pembilang data ditetapkan semula kepada sifar.

Memadam semua mesej dalam penimbal:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Kaunter tidak ditetapkan semula.

Mengeluarkan penimbal dan membebaskan memori:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Contoh penggunaan penimbal boleh didapati dalam kod sumber beberapa penapis strim media. Contohnya, dalam penapis MS_L16_ENC, yang menyusun semula bait dalam sampel daripada susunan rangkaian kepada pesanan hos: l16.c

Dalam artikel seterusnya, kita akan melihat isu anggaran beban ticker dan cara menangani beban pengkomputeran yang berlebihan dalam strim media.

Sumber: www.habr.com

Tambah komen