สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11

เนื้อหาของบทความนำมาจากของฉัน ช่องเซน.

สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11

กลไกการเคลื่อนย้ายข้อมูล

  • บล็อกข้อมูล dblk_t
  • ส่งข้อความ mblk_t
  • ฟังก์ชั่นสำหรับการทำงานกับข้อความ mblk_t
  • คิวคิว_t
  • ฟังก์ชั่นสำหรับการทำงานกับคิว Queue_t
  • การเชื่อมต่อตัวกรอง
  • จุดสัญญาณของกราฟการประมวลผลข้อมูล
  • เบื้องหลังกิจกรรมของทิกเกอร์
  • บัฟเฟอร์ (MSBufferizer)
  • ฟังก์ชั่นสำหรับการทำงานกับ MSBufferizer

ในที่สุด статье เราได้พัฒนาตัวกรองของเราเอง บทความนี้จะเน้นที่กลไกภายในสำหรับการย้ายข้อมูลระหว่างตัวกรองสตรีมเมอร์มีเดีย ซึ่งจะทำให้คุณสามารถเขียนตัวกรองที่ซับซ้อนได้โดยใช้ความพยายามน้อยลงในอนาคต

กลไกการเคลื่อนย้ายข้อมูล

การย้ายข้อมูลในสตรีมเมอร์มีเดียดำเนินการโดยใช้คิวที่อธิบายโดยโครงสร้าง คิว_t. สตริงข้อความเช่น mblk_tซึ่งในตัวมันเองไม่มีข้อมูลสัญญาณ แต่ลิงก์ไปยังข้อความก่อนหน้า ถัดไป และบล็อกข้อมูลเท่านั้น นอกจากนี้ ฉันต้องการเน้นเป็นพิเศษว่ายังมีช่องสำหรับลิงก์ไปยังข้อความประเภทเดียวกัน ซึ่งช่วยให้คุณสามารถจัดระเบียบรายการข้อความที่เชื่อมโยงเดี่ยวๆ ได้ เราจะเรียกกลุ่มข้อความที่รวมเป็นหนึ่งเดียวกันโดยรายการดังกล่าวว่าเป็นสิ่งทูเพิล ดังนั้นองค์ประกอบใดๆ ของคิวจึงสามารถเป็นข้อความเดียวได้ mblk_tและอาจเป็นส่วนหัวของข้อความทูเพิล mblk_t. แต่ละข้อความทูเพิลสามารถมีบล็อกข้อมูลวอร์ดของตัวเองได้ เราจะหารือกันว่าทำไมสิ่งอันดับจึงจำเป็นในภายหลัง

ตามที่กล่าวไว้ข้างต้น ข้อความนั้นไม่มีบล็อกข้อมูล แต่มีเพียงตัวชี้ไปยังพื้นที่หน่วยความจำที่เก็บบล็อกไว้เท่านั้น ในส่วนนี้ ภาพรวมของงานของสตรีมสื่อนั้นชวนให้นึกถึงโกดังประตูในการ์ตูนเรื่อง "Monsters, Inc. " ซึ่งประตู (ลิงก์ไปยังห้องข้อมูล) เคลื่อนที่ด้วยความเร็วอย่างบ้าคลั่งไปตามสายพานลำเลียงเหนือศีรษะ ในขณะที่ตัวห้องเอง ยังคงนิ่งเฉย

ตอนนี้เมื่อย้ายตามลำดับชั้นจากล่างขึ้นบนมาพิจารณารายละเอียดเอนทิตีที่ระบุไว้ของกลไกการส่งข้อมูลในสตรีมสื่อ

บล็อกข้อมูล dblk_t

บล็อกข้อมูลประกอบด้วยส่วนหัวและบัฟเฟอร์ข้อมูล ส่วนหัวอธิบายโดยโครงสร้างต่อไปนี้

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

เขตข้อมูลของโครงสร้างประกอบด้วยตัวชี้ไปยังจุดเริ่มต้นของบัฟเฟอร์ จุดสิ้นสุดของบัฟเฟอร์ และฟังก์ชันสำหรับการลบบัฟเฟอร์ข้อมูล องค์ประกอบสุดท้ายในส่วนหัว db_ref — ตัวนับอ้างอิง หากถึงศูนย์ จะทำหน้าที่เป็นสัญญาณให้ลบบล็อกนี้ออกจากหน่วยความจำ หากบล็อกข้อมูลถูกสร้างขึ้นโดยฟังก์ชัน datab_alloc() จากนั้นบัฟเฟอร์ข้อมูลจะถูกวางไว้ในหน่วยความจำทันทีหลังจากส่วนหัว ในกรณีอื่นๆ ทั้งหมด บัฟเฟอร์สามารถแยกออกจากกัน บัฟเฟอร์ข้อมูลจะมีตัวอย่างสัญญาณหรือข้อมูลอื่นที่เราต้องการประมวลผลด้วยตัวกรอง

อินสแตนซ์ใหม่ของบล็อกข้อมูลถูกสร้างขึ้นโดยใช้ฟังก์ชัน:

dblk_t *datab_alloc(int size);

ในฐานะที่เป็นพารามิเตอร์อินพุต จะมีการกำหนดขนาดของข้อมูลที่บล็อกจะจัดเก็บ มีการจัดสรรหน่วยความจำเพิ่มเติมเพื่อวางส่วนหัว - โครงสร้าง - ที่จุดเริ่มต้นของหน่วยความจำที่จัดสรร ข้อมูล. แต่เมื่อใช้ฟังก์ชันอื่น สิ่งนี้ไม่ได้เกิดขึ้นเสมอไป ในบางกรณี บัฟเฟอร์ข้อมูลอาจแยกจากส่วนหัวของบล็อกข้อมูล เมื่อสร้างโครงสร้าง ฟิลด์จะได้รับการกำหนดค่าเพื่อให้ฟิลด์นั้น db_base ชี้ไปที่จุดเริ่มต้นของพื้นที่ข้อมูลและ db_lim จนจบ จำนวนลิงก์ db_ref ถูกตั้งค่าเป็นหนึ่ง ตัวชี้ฟังก์ชันล้างข้อมูลถูกตั้งค่าเป็นศูนย์

ข่าวสาร mblk_t

ตามที่ระบุไว้ องค์ประกอบคิวเป็นประเภท mblk_t, มันถูกกำหนดไว้ดังนี้:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

โครงสร้าง mblk_t มีตัวชี้ที่จุดเริ่มต้น b_ก่อนหน้า, b_ถัดไปซึ่งจำเป็นในการจัดระเบียบรายการที่มีการเชื่อมโยงแบบทวีคูณ (ซึ่งก็คือคิว คิว_t).

จากนั้นก็มาถึงตัวชี้ b_ต่อซึ่งใช้เมื่อข้อความเป็นส่วนหนึ่งของทูเพิลเท่านั้น สำหรับข้อความสุดท้ายในทูเปิล ตัวชี้นี้ยังคงเป็นโมฆะ

ต่อไปเราจะเห็นตัวชี้ไปยังบล็อกข้อมูล b_datapซึ่งมีข้อความอยู่ ตามด้วยตัวชี้ไปยังพื้นที่ภายในบัฟเฟอร์ข้อมูลบล็อก สนาม b_rptr ระบุตำแหน่งที่จะอ่านข้อมูลจากบัฟเฟอร์ สนาม b_wptr ระบุตำแหน่งที่จะดำเนินการเขียนไปยังบัฟเฟอร์

ฟิลด์ที่เหลือมีลักษณะเป็นบริการและไม่เกี่ยวข้องกับการทำงานของกลไกการถ่ายโอนข้อมูล

ด้านล่างนี้เป็นข้อความเดียวที่มีชื่อ m1 และบล็อกข้อมูล d1.
สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11
รูปต่อไปนี้แสดงข้อความสามรายการ m1, ม1_1, ม1_2.
สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11

ฟังก์ชั่นการส่งข้อความ mblk_t

ข้อความใหม่ mblk_t สร้างโดยฟังก์ชัน:

mblk_t *allocb(int size, int pri); 

เธอวางข้อความใหม่ไว้ในความทรงจำ mblk_t ด้วยบล็อกข้อมูลตามขนาดที่กำหนด ขนาดอาร์กิวเมนต์ที่สอง - PRI ไม่ได้ใช้ในไลบรารีเวอร์ชันนี้ มันควรจะยังคงเป็นศูนย์ ในระหว่างการทำงานของฟังก์ชัน หน่วยความจำจะถูกจัดสรรให้กับโครงสร้างของข้อความใหม่และฟังก์ชันจะถูกเรียกใช้ mblk_init()ซึ่งจะรีเซ็ตฟิลด์ทั้งหมดของอินสแตนซ์ที่สร้างขึ้นของโครงสร้าง จากนั้นใช้สิ่งที่กล่าวมาข้างต้น datab_alloc()จะสร้างบัฟเฟอร์ข้อมูล หลังจากนั้นฟิลด์ในโครงสร้างจะถูกกำหนดค่า:

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

ที่เอาต์พุต เราได้รับข้อความใหม่พร้อมฟิลด์ที่เตรียมใช้งานและบัฟเฟอร์ข้อมูลว่างเปล่า หากต้องการเพิ่มข้อมูลลงในข้อความ คุณต้องคัดลอกข้อมูลดังกล่าวไปยังบัฟเฟอร์บล็อกข้อมูล:

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

ที่ไหน ข้อมูล เป็นตัวชี้ไปยังแหล่งข้อมูลและ ขนาด - ขนาดของพวกเขา
จากนั้นคุณจะต้องอัปเดตตัวชี้ไปยังจุดเขียนเพื่อให้ชี้ไปที่จุดเริ่มต้นของพื้นที่ว่างในบัฟเฟอร์อีกครั้ง:

msg->b_wptr = msg->b_wptr + size

หากคุณต้องการสร้างข้อความจากบัฟเฟอร์ที่มีอยู่โดยไม่ต้องคัดลอก ให้ใช้ฟังก์ชัน:

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

หลังจากสร้างข้อความและโครงสร้างของบล็อกข้อมูลแล้ว ฟังก์ชันจะกำหนดค่าตัวชี้ไปยังข้อมูลตามที่อยู่ บัฟ. เหล่านั้น. ในกรณีนี้บัฟเฟอร์ข้อมูลจะไม่อยู่หลังฟิลด์ส่วนหัวของบล็อกข้อมูล เช่นเดียวกับกรณีเมื่อสร้างบล็อกข้อมูลด้วยฟังก์ชัน datab_alloc(). บัฟเฟอร์ที่มีข้อมูลที่ส่งไปยังฟังก์ชันจะยังคงอยู่ในตำแหน่งเดิม แต่ด้วยความช่วยเหลือของพอยน์เตอร์ บัฟเฟอร์ดังกล่าวจะถูกแนบไปกับส่วนหัวของบล็อกข้อมูลที่สร้างขึ้นใหม่ และตามด้วยข้อความ

ถึงหนึ่งข้อความ mblk_t บล็อกข้อมูลหลายบล็อกสามารถต่อกันตามลำดับได้ สิ่งนี้ทำได้โดยฟังก์ชัน:

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

mp — ข้อความที่จะเพิ่มบล็อคข้อมูลอื่นเข้าไป
ข้อมูล — ตัวชี้ไปที่บล็อก สำเนาซึ่งจะถูกเพิ่มลงในข้อความ
ขนาด — ขนาดข้อมูล
เบาะ — แฟล็กที่ขนาดของหน่วยความจำที่จัดสรรจะต้องจัดแนวตามขอบเขต 4 ไบต์ (การเติมจะดำเนินการด้วยศูนย์)

หากมีพื้นที่ว่างเพียงพอในบัฟเฟอร์ข้อมูลข้อความที่มีอยู่ ข้อมูลใหม่จะถูกวางด้านหลังข้อมูลที่มีอยู่แล้ว หากมีพื้นที่ว่างในบัฟเฟอร์ข้อมูลข้อความน้อยกว่า ขนาดจากนั้นข้อความใหม่จะถูกสร้างขึ้นโดยมีขนาดบัฟเฟอร์เพียงพอ และข้อมูลจะถูกคัดลอกไปยังบัฟเฟอร์ นี่คือข้อความใหม่ ซึ่งเชื่อมโยงกับข้อความต้นฉบับโดยใช้พอยน์เตอร์ b_ต่อ. ในกรณีนี้ ข้อความจะกลายเป็นสิ่งอันดับ

หากคุณต้องการเพิ่มบล็อกข้อมูลอื่นให้กับทูเพิล คุณจะต้องใช้ฟังก์ชัน:

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

เธอจะพบข้อความสุดท้ายในทูเพิล (เขามี b_ต่อ จะเป็นโมฆะ) และจะเรียกใช้ฟังก์ชันสำหรับข้อความนี้ ภาคผนวก().

คุณสามารถค้นหาขนาดของข้อมูลในข้อความหรือทูเพิลได้โดยใช้ฟังก์ชัน:

int msgdsize(const mblk_t *mp);

มันจะวนซ้ำข้อความทั้งหมดในทูเพิลและส่งคืนจำนวนข้อมูลทั้งหมดในบัฟเฟอร์ข้อมูลของข้อความเหล่านั้น สำหรับแต่ละข้อความ จำนวนข้อมูลจะถูกคำนวณดังนี้:

 mp->b_wptr - mp->b_rptr

หากต้องการรวมสองสิ่งอันดับ ให้ใช้ฟังก์ชัน:

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

เธอต่อท้ายสิ่งอันดับ นิวม จนถึงส่วนหางของทูเพิล mp และส่งคืนพอยน์เตอร์ไปยังข้อความสุดท้ายของทูเพิลผลลัพธ์

หากจำเป็น tuple สามารถเปลี่ยนให้เป็นข้อความเดียวด้วยบล็อกข้อมูลเดียว ซึ่งทำได้โดยฟังก์ชัน:

void msgpullup(mblk_t *mp,int len);

ถ้าเถียง len คือ -1 ดังนั้นขนาดของบัฟเฟอร์ที่จัดสรรจะถูกกำหนดโดยอัตโนมัติ ถ้า len เป็นจำนวนบวก บัฟเฟอร์ขนาดนี้จะถูกสร้างขึ้น และข้อมูลข้อความทูเพิลจะถูกคัดลอกลงไป หากบัฟเฟอร์หมด การคัดลอกจะหยุดอยู่แค่นั้น ข้อความแรกของทูเพิลจะได้รับบัฟเฟอร์ขนาดใหม่พร้อมข้อมูลที่คัดลอก ข้อความที่เหลือจะถูกลบและหน่วยความจำจะกลับสู่ฮีป

เมื่อลบโครงสร้าง mblk_t จำนวนการอ้างอิงของบล็อกข้อมูลจะถูกนำมาพิจารณาหากเมื่อมีการโทร ฟรีบี() กลายเป็นศูนย์ จากนั้นบัฟเฟอร์ข้อมูลจะถูกลบไปพร้อมกับอินสแตนซ์ mblk_tซึ่งชี้ไปที่มัน

การเริ่มต้นฟิลด์ของข้อความใหม่:

void mblk_init(mblk_t *mp);

เพิ่มข้อมูลอีกชิ้นลงในข้อความ:

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

หากข้อมูลใหม่ไม่พอดีกับพื้นที่ว่างของบัฟเฟอร์ข้อมูลข้อความ ข้อความที่สร้างขึ้นแยกต่างหากพร้อมบัฟเฟอร์ขนาดที่ต้องการจะถูกแนบไปกับข้อความ (ตัวชี้ไปยังข้อความที่เพิ่มถูกตั้งค่าไว้ในข้อความแรก) และ ข้อความกลายเป็นสิ่งอันดับ

การเพิ่มข้อมูลลงในทูเพิล:

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

ฟังก์ชันเรียกใช้ appendb() ในลูป

รวมสองสิ่งอันดับเป็นหนึ่ง:

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

ข่าวสาร นิวม จะแนบไปกับ mp.

การทำสำเนาข้อความเดียว:

mblk_t *copyb(const mblk_t *mp);

คัดลอกทูเพิลพร้อมบล็อกข้อมูลทั้งหมดให้เสร็จสมบูรณ์:

mblk_t *copymsg(const mblk_t *mp);

องค์ประกอบของทูเพิลถูกคัดลอกโดยฟังก์ชัน คัดลอกบี().

สร้างสำเนาข้อความอย่างง่ายดาย mblk_t. ในกรณีนี้ บล็อกข้อมูลจะไม่ถูกคัดลอก แต่ตัวนับอ้างอิงจะเพิ่มขึ้น db_ref:

mblk_t *dupb(mblk_t *mp);

สร้างสำเนาทูเพิลแบบไลท์เวท บล็อกข้อมูลจะไม่ถูกคัดลอก มีเพียงตัวนับอ้างอิงเท่านั้นที่จะเพิ่มขึ้น db_ref:

mblk_t *dupmsg(mblk_t* m);

การรวมข้อความทั้งหมดของ tuple ไว้ในข้อความเดียว:

void msgpullup(mblk_t *mp,size_t len);

ถ้าข้อโต้แย้ง 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 ซึ่งจะชี้ไปที่องค์ประกอบแรก (ข้อความ) ในคิว ฟิลด์ที่สองของโครงสร้างคือตัวนับข้อความในคิว
รูปด้านล่างแสดงคิวชื่อ q1 ซึ่งมี 4 ข้อความ m1, m2, m3, m4
สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11
รูปต่อไปนี้แสดงคิวชื่อ q1 ซึ่งมี 4 ข้อความ m1,m2,m3,m4 ข้อความ m2 เป็นส่วนหัวของทูเพิลที่มีข้อความเพิ่มเติมอีกสองข้อความ m2_1 และ m2_2

สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 11

ฟังก์ชั่นสำหรับการทำงานกับคิว Queue_t

การเริ่มต้นคิว:

void qinit(queue_t *q);

สนาม _q_สต็อปเปอร์ (ต่อไปนี้เราจะเรียกว่า "ตัวหยุด") ได้รับการเริ่มต้นโดยฟังก์ชัน mblk_init()องค์ประกอบก่อนหน้าและตัวชี้องค์ประกอบถัดไปจะถูกปรับให้ชี้ไปที่ตัวมันเอง ตัวนับองค์ประกอบคิวถูกรีเซ็ตเป็นศูนย์

การเพิ่มองค์ประกอบใหม่ (ข้อความ):

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

องค์ประกอบใหม่ m ถูกเพิ่มที่ส่วนท้ายของรายการ ตัวชี้องค์ประกอบจะถูกปรับเพื่อให้ตัวหยุดกลายเป็นองค์ประกอบถัดไป และกลายเป็นองค์ประกอบก่อนหน้าสำหรับตัวหยุด ตัวนับองค์ประกอบคิวจะเพิ่มขึ้น

การดึงองค์ประกอบจากคิว:

mblk_t * getq(queue_t *q); 

ข้อความที่มาหลังจากเรียกตัวหยุด และตัวนับองค์ประกอบจะลดลง หากไม่มีองค์ประกอบในคิวยกเว้นตัวหยุด ดังนั้น 0 จะถูกส่งกลับ

การแทรกข้อความลงในคิว:

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

Элемент mp แทรกไว้หน้าองค์ประกอบ EMP. ถ้า EMP=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(คิว, ม.) ด้วยตัวชี้ว่างไปยังข้อความ ฟังก์ชันจะวนซ้ำ โปรแกรมของคุณจะหยุดทำงาน มีพฤติกรรมคล้ายกัน ms_queue_next(คิว, ม.).

การเชื่อมต่อตัวกรอง

คิวที่อธิบายไว้ข้างต้นใช้เพื่อส่งข้อความจากตัวกรองหนึ่งไปยังอีกตัวกรองหนึ่งหรือจากตัวกรองหนึ่งไปยังหลายตัวกรอง ตัวกรองและการเชื่อมต่อจะสร้างกราฟกำกับ อินพุตหรือเอาต์พุตของตัวกรองจะเรียกว่าคำทั่วไปว่า "พิน" เพื่ออธิบายลำดับที่ตัวกรองเชื่อมต่อกัน สตรีมมีเดียจะใช้แนวคิดของ "จุดสัญญาณ" จุดสัญญาณเป็นโครงสร้าง _MSCพอยต์ซึ่งมีตัวชี้ไปยังตัวกรองและหมายเลขของพินตัวใดตัวหนึ่ง ดังนั้นจึงอธิบายการเชื่อมต่อของหนึ่งในอินพุตหรือเอาต์พุตของตัวกรอง

จุดสัญญาณของกราฟการประมวลผลข้อมูล

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

หมุดตัวกรองจะมีหมายเลขเริ่มต้นจากศูนย์

การเชื่อมต่อของพินสองตัวด้วยคิวข้อความนั้นอธิบายไว้ในโครงสร้าง _MSคิวซึ่งมีคิวข้อความและตัวชี้ไปยังจุดสัญญาณสองจุดที่เชื่อมต่ออยู่:

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และ edges คืออินสแตนซ์ของลิงก์ MSQueue.

เบื้องหลังกิจกรรมของทิกเกอร์

เมื่อฉันบอกคุณว่าสัญลักษณ์เป็นตัวกรองแหล่งที่มาของเห็บ มันไม่ใช่ความจริงทั้งหมดเกี่ยวกับเรื่องนี้ สัญลักษณ์คือวัตถุที่เรียกใช้ฟังก์ชันบนนาฬิกา กระบวนการ() ตัวกรองทั้งหมดของวงจร (กราฟ) ที่เชื่อมต่ออยู่ เมื่อเราเชื่อมต่อทิกเกอร์กับตัวกรองกราฟในโปรแกรม C เราจะแสดงกราฟที่จะควบคุมต่อจากนี้ไปจนกว่าเราจะปิดทิกเกอร์ หลังจากเชื่อมต่อแล้ว ทิกเกอร์จะเริ่มตรวจสอบกราฟที่ได้รับมอบหมายให้ดูแล โดยรวบรวมรายการตัวกรองที่รวมกราฟนั้นไว้ เพื่อไม่ให้ "นับ" ตัวกรองเดียวกันสองครั้ง ระบบจะทำเครื่องหมายตัวกรองที่ตรวจพบโดยทำเครื่องหมายในช่อง เห็น. การค้นหาดำเนินการโดยใช้ตารางลิงก์ที่แต่ละตัวกรองมี

ในระหว่างการชมกราฟเบื้องต้น ทิกเกอร์จะตรวจสอบว่าในบรรดาตัวกรองต่างๆ มีอย่างน้อยหนึ่งตัวที่ทำหน้าที่เป็นแหล่งที่มาของบล็อกข้อมูลหรือไม่ หากไม่มีเลย จะถือว่ากราฟไม่ถูกต้องและทิกเกอร์ขัดข้อง

หากกราฟกลายเป็น "ถูกต้อง" สำหรับแต่ละตัวกรองที่พบ ฟังก์ชันนี้จะถูกเรียกใช้เพื่อการเริ่มต้น กระบวนการล่วงหน้า (). ทันทีที่ถึงเวลาสำหรับรอบการประมวลผลถัดไป (ทุกๆ 10 มิลลิวินาทีตามค่าเริ่มต้น) ทิกเกอร์จะเรียกใช้ฟังก์ชัน กระบวนการ() สำหรับตัวกรองแหล่งที่มาที่พบก่อนหน้านี้ทั้งหมด จากนั้นสำหรับตัวกรองที่เหลืออยู่ในรายการ หากตัวกรองมีลิงก์อินพุต ให้เรียกใช้ฟังก์ชัน กระบวนการ() ทำซ้ำจนกว่าคิวลิงก์อินพุตจะว่างเปล่า หลังจากนั้น ระบบจะย้ายไปยังตัวกรองถัดไปในรายการและ "เลื่อน" จนกว่าลิงก์อินพุตจะไม่มีข้อความ สัญลักษณ์จะย้ายจากตัวกรองหนึ่งไปอีกตัวกรองหนึ่งจนกว่ารายการจะสิ้นสุด เสร็จสิ้นการประมวลผลของวงจร

ตอนนี้เราจะกลับไปที่สิ่งอันดับและพูดคุยเกี่ยวกับสาเหตุที่เพิ่มเอนทิตีดังกล่าวลงในสตรีมเมอร์สื่อ โดยทั่วไป จำนวนข้อมูลที่อัลกอริธึมที่ทำงานภายในตัวกรองต้องการนั้นไม่ตรงกันและไม่ได้เป็นหลายเท่าของขนาดของบัฟเฟอร์ข้อมูลที่ได้รับจากอินพุต ตัวอย่างเช่น เรากำลังเขียนตัวกรองที่ทำการแปลงฟูเรียร์ที่รวดเร็ว ซึ่งตามคำจำกัดความแล้วสามารถประมวลผลได้เฉพาะบล็อกข้อมูลที่มีขนาดเป็นกำลังสองเท่านั้น ปล่อยให้มันเป็น 512 นับ หากข้อมูลถูกสร้างขึ้นโดยช่องโทรศัพท์ บัฟเฟอร์ข้อมูลของแต่ละข้อความที่อินพุตจะนำตัวอย่างสัญญาณ 160 ตัวอย่างมาให้เรา ไม่ควรรวบรวมข้อมูลจากอินพุตจนกว่าจะมีข้อมูลตามจำนวนที่ต้องการ แต่ในกรณีนี้ จะเกิดการชนกันกับทิกเกอร์ ซึ่งจะพยายามเลื่อนตัวกรองไม่สำเร็จจนกว่าลิงก์อินพุตจะว่างเปล่า ก่อนหน้านี้ เราได้กำหนดกฎนี้เป็นหลักการที่สามของตัวกรอง ตามหลักการนี้ ฟังก์ชัน process() ของตัวกรองจะต้องดึงข้อมูลทั้งหมดจากคิวอินพุต

นอกจากนี้ เป็นไปไม่ได้ที่จะดึงตัวอย่างเพียง 512 ตัวอย่างจากอินพุต เนื่องจากคุณสามารถรับได้เฉพาะทั้งบล็อคเท่านั้น กล่าวคือ ตัวกรองจะต้องเก็บตัวอย่าง 640 ตัวอย่างและใช้ 512 ตัวอย่าง ส่วนที่เหลือก่อนที่จะสะสมข้อมูลส่วนใหม่ ดังนั้นตัวกรองของเรานอกเหนือจากงานหลักแล้ว จะต้องมีการดำเนินการเสริมสำหรับการจัดเก็บข้อมูลอินพุตระดับกลาง ผู้พัฒนาสตรีมสื่อและวิธีแก้ปัญหาทั่วไปนี้ได้พัฒนาวัตถุพิเศษ - MSBufferizer (บัฟเฟอร์) ซึ่งแก้ไขปัญหานี้โดยใช้สิ่งอันดับ

บัฟเฟอร์ (MSBufferizer)

นี่คือออบเจ็กต์ที่จะสะสมข้อมูลอินพุตภายในตัวกรอง และเริ่มส่งเพื่อประมวลผลทันทีที่ปริมาณข้อมูลเพียงพอที่จะเรียกใช้อัลกอริทึมตัวกรอง ในขณะที่บัฟเฟอร์กำลังสะสมข้อมูล ตัวกรองจะทำงานในโหมดไม่ได้ใช้งาน โดยไม่ต้องใช้พลังการประมวลผลของโปรเซสเซอร์จนหมด แต่ทันทีที่ฟังก์ชันการอ่านจากตัวบัฟเฟอร์ส่งคืนค่าอื่นที่ไม่ใช่ศูนย์ ฟังก์ชัน process() ของตัวกรองจะเริ่มรับและประมวลผลข้อมูลจากตัวบัฟเฟอร์ตามขนาดที่ต้องการ จนกระทั่งหมด
ข้อมูลที่ยังไม่จำเป็นจะยังคงอยู่ในบัฟเฟอร์เป็นองค์ประกอบแรกของทูเพิล ซึ่งบล็อกข้อมูลอินพุตที่ตามมาจะแนบไปกับบล็อกนั้น

โครงสร้างที่อธิบายบัฟเฟอร์:

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

ฟังก์ชั่นสำหรับการทำงานกับ MSBufferizer

การสร้างอินสแตนซ์บัฟเฟอร์ใหม่:

MSBufferizer * ms_bufferizer_new(void);

หน่วยความจำได้รับการจัดสรร เริ่มต้นใน ms_bufferizer_init() และตัวชี้จะถูกส่งกลับ

ฟังก์ชั่นการเริ่มต้น:

void ms_bufferizer_init(MSBufferizer *obj); 

กำลังเริ่มต้นคิว q, สนาม ขนาด ถูกตั้งค่าเป็นศูนย์

การเพิ่มข้อความ:

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

ข้อความ m ถูกเพิ่มเข้าไปในคิว เพิ่มขนาดบล็อกข้อมูลที่คำนวณได้ ขนาด.

ถ่ายโอนข้อความทั้งหมดจากคิวข้อมูลลิงก์ไปยังบัฟเฟอร์ q:

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

การถ่ายโอนข้อความจากลิงก์ q ในบัฟเฟอร์จะดำเนินการโดยใช้ฟังก์ชัน ms_bufferizer_put().

อ่านจากบัฟเฟอร์:

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

หากขนาดของข้อมูลที่สะสมในบัฟเฟอร์น้อยกว่าที่ร้องขอ (ดาต้าเลน) จากนั้นฟังก์ชันจะส่งกลับค่าศูนย์ ข้อมูลจะไม่ถูกคัดลอกไปยังข้อมูล มิฉะนั้น จะดำเนินการคัดลอกข้อมูลจากสิ่งอันดับที่อยู่ในบัฟเฟอร์ตามลำดับ หลังจากการคัดลอก 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.ค

ในบทความถัดไป เราจะพิจารณาปัญหาของการประมาณการโหลดทิกเกอร์และวิธีจัดการกับโหลดการประมวลผลที่มากเกินไปในสตรีมมีเดีย

ที่มา: will.com

เพิ่มความคิดเห็น