Mediastreamer2 VoIP хөдөлгүүртэй танилцаж байна. 11-р хэсэг

Нийтлэлийн материалыг миний нийтлэлээс авсан болно zen суваг.

Mediastreamer2 VoIP хөдөлгүүртэй танилцаж байна. 11-р хэсэг

Өгөгдлийн хөдөлгөөний механизм

  • Өгөгдлийн блок dblk_t
  • mblk_t гэсэн мессеж
  • Мессежтэй ажиллах функцууд mblk_t
  • Дараалал queue_t
  • Дараалалтай ажиллах функцууд queue_t
  • Шүүлтүүрийг холбох
  • Мэдээлэл боловсруулах графикийн дохионы цэг
  • Тайкерийн тайзны арын үйл ажиллагаа
  • Буфержуулагч (MSBufferizer)
  • MSBufferizer-тэй ажиллах функцууд

Хамгийн сүүлд нийтлэл Бид өөрсдийн шүүлтүүрийг боловсруулсан. Энэ нийтлэл нь медиа дамжуулагч шүүлтүүрүүдийн хооронд өгөгдлийг шилжүүлэх дотоод механизмд анхаарлаа хандуулах болно. Ингэснээр та ирээдүйд бага хүчин чармайлтаар боловсронгуй шүүлтүүр бичих боломжтой болно.

Өгөгдлийн хөдөлгөөний механизм

Медиа дамжуулагч дахь өгөгдлийн хөдөлгөөнийг бүтцийн тодорхойлсон дарааллыг ашиглан гүйцэтгэдэг дараалал_t. гэх мэт мессежийн мөрүүд mblk_t, тэдгээр нь өөрөө дохионы өгөгдлийг агуулаагүй, зөвхөн өмнөх, дараагийн мессеж болон өгөгдлийн блок руу холбодог. Нэмж дурдахад, ижил төрлийн мессежийг холбох талбар байдаг бөгөөд энэ нь танд дангаар нь холбогдсон мессежийн жагсаалтыг зохион байгуулах боломжийг олгодог. Ийм жагсаалтаар нэгтгэсэн мессежийн бүлгийг бид tuple гэж нэрлэх болно. Тиймээс дарааллын аль ч элемент нь нэг мессеж байж болно mblk_t, магадгүй зурвасын толгой байж болно mblk_t. Tuple мессеж бүр өөрийн тойргийн мэдээллийн блоктой байж болно. Туплер яагаад хэрэгтэй байгааг бид дараа нь хэлэлцэх болно.

Дээр дурьдсанчлан, мессеж өөрөө өгөгдлийн блок агуулаагүй бөгөөд үүний оронд зөвхөн блок хадгалагдаж буй санах ойн талбарт заагчийг агуулна. Энэ хэсэгт медиа дамжуулагчийн ажлын ерөнхий дүр зураг нь "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_дараагийн, давхар холбоос бүхий жагсаалтыг зохион байгуулахад шаардлагатай (энэ нь дараалал юм дараалал_t).

Дараа нь заагч ирдэг b_үргэлжлэл, энэ нь зурвас нь tuple-ийн хэсэг байх үед л ашиглагддаг. Tuple дахь сүүлчийн мессежийн хувьд энэ заагч хоосон хэвээр байна.

Дараа нь бид өгөгдлийн блок руу заагчийг харж байна b_өгөгдөл, үүний тухай мессеж байгаа. Үүний араас блок өгөгдлийн буфер доторх талбарт заагч орно. Талбай b_rptr буферээс өгөгдөл унших байршлыг зааж өгнө. Талбай b_wptr буферт бичих үйлдлийг гүйцэтгэх байршлыг заана.

Үлдсэн талбарууд нь үйлчилгээний шинж чанартай бөгөөд өгөгдөл дамжуулах механизмын ажиллагаатай холбоогүй болно.

Доорх нэртэй ганц мессеж байна m1 болон өгөгдлийн блок d1.
Mediastreamer2 VoIP хөдөлгүүртэй танилцаж байна. 11-р хэсэг
Дараах зурагт гурван мессежийн багцыг харуулав m1, m1_1, m1_2.
Mediastreamer2 VoIP хөдөлгүүртэй танилцаж байна. 11-р хэсэг

Мессежийн функцууд mblk_t

Шинэ мессеж mblk_t функцээр үүсгэгдсэн:

mblk_t *allocb(int size, int pri); 

тэр санах ойд шинэ зурвас байрлуулдаг mblk_t заасан хэмжээтэй өгөгдлийн блоктой хэмжээ, хоёр дахь аргумент - при номын сангийн энэ хувилбарт ашиглагдаагүй. Энэ нь тэг хэвээр байх ёстой. Функцийн үйл ажиллагааны явцад шинэ мессежийн бүтцэд санах ойг хуваарилж, функцийг дуудах болно mblk_init(), энэ нь бүтцийн үүсгэсэн жишээний бүх талбарыг дахин тохируулж, дараа нь дээр дурдсаныг ашиглана datab_alloc(), өгөгдлийн буфер үүсгэх болно. Үүний дараа бүтцийн талбаруудыг тохируулах болно:

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

Гаралт дээр бид эхлүүлсэн талбарууд болон хоосон өгөгдлийн буфер бүхий шинэ мессеж хүлээн авдаг. Мессежид өгөгдөл нэмэхийн тулд та үүнийг өгөгдлийн блок буфер рүү хуулах хэрэгтэй.

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

хаана мэдээ нь өгөгдлийн эх сурвалжийн заагч бөгөөд хэмжээ - тэдний хэмжээ.
Дараа нь та заагчийг бичих цэг рүү шинэчлэх хэрэгтэй бөгөөд ингэснээр буфер дахь чөлөөт хэсгийн эхлэл рүү дахин чиглэнэ.

msg->b_wptr = msg->b_wptr + size

Хэрэв та одоо байгаа буферээс хуулахгүйгээр мессеж үүсгэх шаардлагатай бол дараах функцийг ашиглана уу.

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

Уг функц нь мессеж болон өгөгдлийн блокийн бүтцийг үүсгэсний дараа хаяг дээрх өгөгдөлд заагчаа тохируулах болно. булф. Тэдгээр. Энэ тохиолдолд өгөгдлийн буфер нь функцээр өгөгдлийн блок үүсгэх үед байсан шиг өгөгдлийн блокийн толгой талбаруудын дараа байрлахгүй. datab_alloc(). Функцэд дамжуулагдсан өгөгдөл бүхий буфер нь байсан газартаа үлдэх боловч заагчуудын тусламжтайгаар өгөгдлийн блокийн шинээр үүсгэсэн толгой хэсэгт хавсаргах болно.

Нэг мессеж рүү mblk_t Хэд хэдэн өгөгдлийн блокуудыг дараалан холбож болно. Үүнийг дараах функцээр гүйцэтгэдэг.

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

mp — өөр өгөгдлийн блок нэмэх мессеж;
мэдээ — блок руу заагч, түүний хуулбарыг зурваст нэмэх болно;
хэмжээ - өгөгдлийн хэмжээ;
дэвсгэр — Хуваарилагдсан санах ойн хэмжээг 4 байт хилийн дагуу зэрэгцүүлэх ёстой гэсэн туг (загварыг тэгээр хийнэ).

Хэрэв одоо байгаа мессежийн өгөгдлийн буферт хангалттай зай байгаа бол шинэ өгөгдлийг аль хэдийн байгаа өгөгдлийн ард буулгах болно. Хэрэв мессежийн өгөгдлийн буферт зай бага байвал хэмжээ, дараа нь хангалттай буфер хэмжээтэй шинэ мессеж үүсгэгдэж, өгөгдлийг буфер рүү хуулна. Энэ бол заагч ашиглан эхтэй нь холбосон шинэ зурвас юм b_үргэлжлэл. Энэ тохиолдолд мессеж нь tuple болж хувирдаг.

Хэрэв та өгөгдлийн блок руу өөр блок нэмэх шаардлагатай бол дараах функцийг ашиглах хэрэгтэй.

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

тэр tuple дахь сүүлчийн мессежийг олох болно (түүнд байна b_үргэлжлэл null байх болно) бөгөөд энэ мессежийн функцийг дуудах болно appendb().

Та дараах функцийг ашиглан мессеж эсвэл өгөгдлийн хэмжээг мэдэж болно.

int msgdsize(const mblk_t *mp);

энэ нь tuple дахь бүх мессежийг дамжуулж, тэдгээр мессежийн өгөгдлийн буфер дэх нийт өгөгдлийн хэмжээг буцаана. Мессеж бүрийн хувьд өгөгдлийн хэмжээг дараах байдлаар тооцоолно.

 mp->b_wptr - mp->b_rptr

Хоёр залгуурыг нэгтгэхийн тулд функцийг ашиглана уу:

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

тэр хэлхээг хавсаргана шинэ tuple-ийн сүүл рүү mp мөн үр дүнгийн tuple-ийн сүүлчийн зурваст заагчийг буцаана.

Шаардлагатай бол залгуурыг нэг блок өгөгдлийн хамт нэг мессеж болгон хувиргах боломжтой бөгөөд үүнийг функцээр гүйцэтгэдэг.

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

Бүх өгөгдлийн блок бүхий tuple-г бүрэн хуулах:

mblk_t *copymsg(const mblk_t *mp);

Түлшний элементүүдийг функцээр хуулж авдаг copyb().

Мессежийн хялбар хуулбарыг үүсгэ mblk_t. Энэ тохиолдолд өгөгдлийн блок хуулахгүй, харин түүний лавлагааны тоолуур нэмэгддэг db_ref:

mblk_t *dupb(mblk_t *mp);

Tuple-ийн хөнгөн хуулбарыг хийх. Өгөгдлийн блокуудыг хуулахгүй, зөвхөн лавлагааны тоологчийг нэмэгдүүлнэ db_ref:

mblk_t *dupmsg(mblk_t* m);

Tuple-ийн бүх мессежийг нэг зурваст наалдуулах:

void msgpullup(mblk_t *mp,size_t len);

Хэрэв маргаан бол лэн -1 бол хуваарилагдсан буферийн хэмжээг автоматаар тодорхойлно.

Мессежийг устгаж байна, tuple:

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

Дараалал дараалал_t

Медиа дамжуулагч дахь мессежийн дарааллыг дугуй хэлбэртэй давхар холбосон жагсаалт хэлбэрээр хэрэгжүүлдэг. Жагсаалтын элемент бүр дохионы дээж бүхий өгөгдлийн блок руу заагчийг агуулна. Зөвхөн өгөгдлийн блок руу чиглэсэн заагчууд ээлжлэн хөдөлдөг бол өгөгдөл өөрөө хөдөлгөөнгүй хэвээр байна. Тэдгээр. зөвхөн тэдгээрийн холбоосыг зөөвөрлөнө.
Дараалалыг дүрсэлсэн бүтэц дараалал_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 гэсэн хоёр мессежийг агуулсан tuple-ийн толгой юм.

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_next(q, м).

Шүүлтүүрийг холбох

Дээр дурдсан дараалал нь мессежийг нэг шүүлтүүрээс нөгөө шүүлтүүр рүү эсвэл нэгээс хэд хэдэн шүүлтүүр рүү дамжуулахад хэрэглэгддэг. Шүүлтүүр ба тэдгээрийн холболтууд нь чиглэсэн график үүсгэдэг. Шүүлтүүрийн оролт эсвэл гаралтыг ерөнхий үг "зүү" гэж нэрлэнэ. Шүүлтүүрүүд хоорондоо холбогдсон дарааллыг тайлбарлахын тулд медиа дамжуулагч нь "дохио цэг" гэсэн ойлголтыг ашигладаг. Дохионы цэг нь бүтэц юм _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 миллисекунд тутамд) ирэх мөчид тикер функцийг дууддаг. процесс() өмнө нь олдсон бүх эх сурвалж шүүлтүүр, дараа нь жагсаалтад үлдсэн шүүлтүүрүүд. Хэрэв шүүлтүүр нь оролтын холбоостой бол функцийг ажиллуулна процесс() оролтын холбоосын дараалал хоосон болтол давтана. Үүний дараа энэ нь жагсаалтын дараагийн шүүлтүүр рүү шилжиж, оролтын холбоосууд мессежгүй болтол "гүйлгэх" болно. Шалгуур нь жагсаалт дуусах хүртэл шүүлтүүрээс шүүлтүүр рүү шилжинэ. Энэ нь мөчлөгийн боловсруулалтыг дуусгана.

Одоо бид tuples руу буцаж, яагаад ийм нэгжийг медиа дамжуулагч дээр нэмсэн талаар ярих болно. Ерөнхийдөө шүүлтүүр дотор ажиллаж буй алгоритмд шаардагдах өгөгдлийн хэмжээ нь давхцдаггүй бөгөөд оролтод хүлээн авсан өгөгдлийн буферийн хэмжээнээс хэд дахин их биш юм. Жишээлбэл, бид Фурьегийн хурдан хувиргалтыг гүйцэтгэдэг шүүлтүүрийг бичиж байгаа бөгөөд энэ нь тодорхойлолтоороо зөвхөн хоёрын хүч бүхий өгөгдлийн блокуудыг боловсруулах боломжтой. 512 тоотой байг. Хэрэв өгөгдөл нь утасны сувгаар үүсгэгдсэн бол оролт дээрх мессеж бүрийн өгөгдлийн буфер нь бидэнд 160 дохионы дээж авчрах болно. Шаардлагатай хэмжээний өгөгдөл байх хүртэл оролтоос өгөгдөл цуглуулахгүй байх нь сонирхол татдаг. Гэхдээ энэ тохиолдолд тикертэй мөргөлдөх бөгөөд энэ нь оролтын холбоос хоосон болтол шүүлтүүрийг гүйлгэх оролдлого амжилтгүй болно. Өмнө нь бид энэ дүрмийг шүүлтүүрийн гурав дахь зарчим гэж тодорхойлсон. Энэ зарчмын дагуу шүүлтүүрийн process() функц нь оролтын дарааллаас бүх өгөгдлийг авах ёстой.

Нэмж дурдахад, оролтоос зөвхөн 512 дээж авах боломжгүй, учир нь та зөвхөн бүхэл блок авах боломжтой. Шүүлтүүр нь 640 дээж авч, 512-ыг нь ашиглах ёстой бөгөөд үлдсэнийг нь мэдээллийн шинэ хэсгийг хуримтлуулах хэрэгтэй. Тиймээс манай шүүлтүүр нь үндсэн ажлаас гадна оролтын өгөгдлийг завсрын хугацаанд хадгалахад туслах үйлдлүүдийг өгөх ёстой. Медиа дамжуулагчийг хөгжүүлэгчид болон энэхүү ерөнхий асуудлын шийдэл нь тусгай объект - MSBufferizer (буферер) -ийг боловсруулсан бөгөөд энэ асуудлыг tuple ашиглан шийддэг.

Буфержуулагч (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); 

Хэрэв буферт хуримтлагдсан өгөгдлийн хэмжээ хүссэн хэмжээнээс бага байвал (өгөгдөлтэй), дараа нь функц тэгийг буцаана, өгөгдлийг өгөгдөлд хуулахгүй. Үгүй бол буферт байрлах залгууруудаас өгөгдлийг дараалан хуулбарлах болно. Хуулбарласаны дараа tuple устгагдаж, санах ойг чөлөөлнө. Мэдээллийн байтыг хуулах үед хуулах ажил дуусна. Хэрэв өгөгдлийн блокийн дунд зай дуусвал энэ зурваст өгөгдлийн блок нь хуулж аваагүй үлдсэн хэсэг хүртэл багасна. Таныг дараагийн удаа залгахад энэ үеэс эхлэн хуулах ажил үргэлжилнэ.

Буферт байгаа өгөгдлийн хэмжээг уншиж байна:

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

сэтгэгдэл нэмэх