استكشاف محرك Mediastreamer2 VoIP. الجزء 11

مادة المقال مأخوذة من بلدي قناة زين.

استكشاف محرك Mediastreamer2 VoIP. الجزء 11

آلية نقل البيانات

  • كتلة البيانات dblk_t
  • رسالة mblk_t
  • وظائف للعمل مع الرسائل mblk_t
  • قائمة الانتظار queue_t
  • وظائف للعمل مع قوائم الانتظار queue_t
  • توصيل المرشحات
  • نقطة إشارة الرسم البياني لمعالجة البيانات
  • أنشطة خلف الكواليس للشريط
  • المخزن المؤقت (MSBufferizer)
  • وظائف للعمل مع MSBufferizer

في الماضي مقالة لقد قمنا بتطوير الفلتر الخاص بنا. ستركز هذه المقالة على الآلية الداخلية لنقل البيانات بين مرشحات دفق الوسائط. سيسمح لك ذلك بكتابة مرشحات متطورة بجهد أقل في المستقبل.

آلية نقل البيانات

يتم تنفيذ حركة البيانات في مشغل الوسائط باستخدام قوائم الانتظار الموضحة في البنية queue_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

كما ذكرنا سابقًا، عناصر قائمة الانتظار من النوع مبلك_ت, يتم تعريفه على النحو التالي:

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_prev, ب_التالي، وهي ضرورية لتنظيم قائمة مرتبطة بشكل مضاعف (وهي قائمة انتظار queue_t).

ثم يأتي المؤشر ب_cont، والذي يُستخدم فقط عندما تكون الرسالة جزءًا من صف. بالنسبة للرسالة الأخيرة في الصف، يظل هذا المؤشر فارغًا.

بعد ذلك نرى مؤشرًا لكتلة البيانات 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 مع كتلة بيانات بالحجم المحدد المقاسات، الحجة الثانية - الحزب الثوري المؤسسي لا تستخدم في هذا الإصدار من المكتبة. يجب أن يبقى صفراً. أثناء تشغيل الوظيفة، سيتم تخصيص الذاكرة لبنية الرسالة الجديدة وسيتم استدعاء الوظيفة مبلك_ينيت()، والذي سيؤدي إلى إعادة تعيين جميع حقول المثيل الذي تم إنشاؤه للبنية ثم استخدام ما سبق ذكره 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*)); 

ستقوم الوظيفة، بعد إنشاء الرسالة وبنية كتلة البيانات، بتكوين مؤشراتها على البيانات الموجودة على العنوان BUF. أولئك. في هذه الحالة، لا يوجد المخزن المؤقت للبيانات بعد حقول الرأس الخاصة بكتلة البيانات، كما كان الحال عند إنشاء كتلة بيانات باستخدام الوظيفة datab_alloc(). سيبقى المخزن المؤقت الذي يحتوي على البيانات التي تم تمريرها إلى الوظيفة في مكانه، ولكن بمساعدة المؤشرات سيتم إرفاقه بالرأس الذي تم إنشاؤه حديثًا لكتلة البيانات، وبالتالي، بالرسالة.

إلى رسالة واحدة mblk_t يمكن ربط العديد من كتل البيانات بالتسلسل. ويتم ذلك عن طريق الدالة:

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

mp — رسالة ستتم إضافة كتلة بيانات أخرى إليها؛
البيانات — مؤشر الكتلة، التي ستتم إضافة نسخة منها إلى الرسالة؛
المقاسات - حجم البيانات؛
وسادة - علامة تشير إلى أنه يجب محاذاة حجم الذاكرة المخصصة على طول حدود 4 بايت (سيتم الحشو بالأصفار).

إذا كانت هناك مساحة كافية في المخزن المؤقت لبيانات الرسالة الموجودة، فسيتم لصق البيانات الجديدة خلف البيانات الموجودة بالفعل. إذا كانت هناك مساحة خالية أقل في المخزن المؤقت لبيانات الرسالة من المقاسات، ثم يتم إنشاء رسالة جديدة بحجم مخزن مؤقت كافٍ ويتم نسخ البيانات إلى المخزن المؤقت الخاص بها. هذه رسالة جديدة، مرتبطة بالرسالة الأصلية باستخدام المؤشر ب_cont. في هذه الحالة، تتحول الرسالة إلى صف.

إذا كنت بحاجة إلى إضافة كتلة أخرى من البيانات إلى المجموعة، فأنت بحاجة إلى استخدام الوظيفة:

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

سوف تجد الرسالة الأخيرة في الصف (لديه ب_cont سيكون فارغًا) وسيقوم باستدعاء الوظيفة لهذه الرسالة إلحاق().

يمكنك معرفة حجم البيانات في رسالة أو صف باستخدام الوظيفة:

int msgdsize(const mblk_t *mp);

سيتم تكراره عبر كافة الرسائل الموجودة في المجموعة وإرجاع إجمالي كمية البيانات الموجودة في مخازن البيانات المؤقتة لتلك الرسائل. يتم حساب كمية البيانات لكل رسالة على النحو التالي:

 mp->b_wptr - mp->b_rptr

للجمع بين صفين، استخدم الدالة:

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

إنها تقوم بإلحاق الصف newm إلى ذيل الصفوف mp ويعيد مؤشرًا إلى الرسالة الأخيرة من الصف الناتج.

إذا لزم الأمر، يمكن تحويل الصف إلى رسالة واحدة تحتوي على كتلة واحدة من البيانات، ويتم ذلك عن طريق الوظيفة:

void msgpullup(mblk_t *mp,int len);

إذا حجة ليون هو -1، ثم يتم تحديد حجم المخزن المؤقت المخصص تلقائيًا. لو ليون إذا كان رقمًا موجبًا، فسيتم إنشاء مخزن مؤقت بهذا الحجم وسيتم نسخ بيانات رسالة المجموعة إليه. في حالة نفاد المخزن المؤقت، سيتوقف النسخ عند هذا الحد. ستتلقى الرسالة الأولى من المجموعة مخزنًا مؤقتًا بحجم جديد يحتوي على البيانات المنسوخة. سيتم حذف الرسائل المتبقية وإرجاع الذاكرة إلى الكومة.

عند حذف الهيكل 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);

إذا لم تتناسب البيانات الجديدة مع المساحة الحرة للمخزن المؤقت لبيانات الرسالة، فسيتم إرفاق رسالة تم إنشاؤها بشكل منفصل مع مخزن مؤقت بالحجم المطلوب بالرسالة (يتم تعيين مؤشر للرسالة المضافة في الرسالة الأولى) و تتحول الرسالة إلى Tuple.

إضافة جزء من البيانات إلى Tuple:

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

تستدعي الدالة appendb()‎ في حلقة.

الجمع بين مجموعتين في واحدة:

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

رسالة 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);

عمل نسخة خفيفة الوزن من Tuple. لا يتم نسخ كتل البيانات، بل تتم زيادة العدادات المرجعية الخاصة بها فقط db_ref:

mblk_t *dupmsg(mblk_t* m);

لصق كافة رسائل الصف في رسالة واحدة:

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

منعطف queue_t

يتم تنفيذ قائمة انتظار الرسائل في مشغل الوسائط كقائمة دائرية مرتبطة بشكل مضاعف. يحتوي كل عنصر قائمة على مؤشر لكتلة بيانات تحتوي على عينات إشارة. اتضح أن المؤشرات إلى كتلة البيانات فقط هي التي تتحرك بدورها، بينما تظل البيانات نفسها بلا حراك. أولئك. يتم نقل الروابط لهم فقط.
هيكل يصف قائمة الانتظار queue_t، ظاهر أدناه:

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

يحتوي الهيكل على حقل - مؤشر _q_stopper اكتب *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_stopper (سنسميها فيما يلي "السدادة") تتم تهيئتها بواسطة الوظيفة مبلك_ينيت()، يتم ضبط العنصر السابق ومؤشر العنصر التالي للإشارة إلى نفسه. تتم إعادة تعيين عداد عنصر قائمة الانتظار إلى الصفر.

إضافة عنصر جديد (الرسائل):

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(ف، م).

توصيل المرشحات

يتم استخدام قائمة الانتظار الموضحة أعلاه لتمرير الرسائل من مرشح إلى آخر أو من مرشح واحد إلى عدة مرشحات. تشكل المرشحات واتصالاتها رسمًا بيانيًا موجهًا. سيتم تسمية الإدخال أو الإخراج للمرشح بالكلمة العامة "pin". لوصف الترتيب الذي يتم به توصيل المرشحات ببعضها البعض، يستخدم مشغل الوسائط مفهوم "نقطة الإشارة". نقطة الإشارة هي الهيكل _MSCPoint، والذي يحتوي على مؤشر للمرشح ورقم أحد أطرافه، وبالتالي فهو يصف اتصال أحد مدخلات أو مخرجات المرشح.

نقطة إشارة الرسم البياني لمعالجة البيانات

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 مللي ثانية افتراضيًا)، يقوم المؤشر باستدعاء الوظيفة عملية() لجميع عوامل التصفية المصدر التي تم العثور عليها مسبقًا، ثم لعوامل التصفية المتبقية في القائمة. إذا كان المرشح يحتوي على روابط الإدخال، فقم بتشغيل الوظيفة عملية() يتكرر حتى تصبح قوائم انتظار ارتباط الإدخال فارغة. بعد ذلك، ينتقل إلى الفلتر التالي في القائمة ويقوم بتمريره حتى تصبح روابط الإدخال خالية من الرسائل. ينتقل المؤشر من عامل تصفية إلى آخر حتى تنتهي القائمة. هذا يكمل معالجة الدورة.

سنعود الآن إلى الصفوف ونتحدث عن سبب إضافة هذا الكيان إلى مشغل الوسائط. بشكل عام، لا تتطابق كمية البيانات التي تتطلبها الخوارزمية التي تعمل داخل المرشح ولا تكون مضاعفًا لحجم مخازن البيانات المؤقتة المستلمة عند الإدخال. على سبيل المثال، نحن نكتب مرشحًا يقوم بإجراء تحويل فورييه سريع، والذي بحكم التعريف يمكنه فقط معالجة كتل البيانات التي يبلغ حجمها قوة اثنين. فليكن 512 التهم. إذا تم إنشاء البيانات بواسطة قناة هاتفية، فإن المخزن المؤقت للبيانات لكل رسالة عند الإدخال سيجلب لنا 160 عينة إشارة. من المغري عدم جمع البيانات من المدخلات حتى تتوفر الكمية المطلوبة من البيانات. ولكن في هذه الحالة، سيحدث تصادم مع المؤشر، الذي سيحاول دون جدوى تمرير عامل التصفية حتى يصبح رابط الإدخال فارغًا. لقد حددنا سابقًا هذه القاعدة باعتبارها المبدأ الثالث للمرشح. وفقًا لهذا المبدأ، يجب أن تأخذ وظيفة عملية المرشح () جميع البيانات من قوائم انتظار الإدخال.

بالإضافة إلى ذلك، لن يكون من الممكن أخذ 512 عينة فقط من المدخلات، حيث يمكنك فقط أخذ كتل كاملة، أي. سيتعين على المرشح أن يأخذ 640 عينة ويستخدم 512 منها، والباقي قبل تجميع جزء جديد من البيانات. وبالتالي، يجب أن يوفر مرشحنا، بالإضافة إلى عمله الرئيسي، إجراءات مساعدة للتخزين الوسيط لبيانات الإدخال. قام مطورو برنامج بث الوسائط وحل هذه المشكلة العامة بتطوير كائن خاص - MSBufferizer (المخزن المؤقت)، والذي يحل هذه المشكلة باستخدام الصفوف.

المخزن المؤقت (MSBufferizer)

هذا هو الكائن الذي سيقوم بتجميع بيانات الإدخال داخل عامل التصفية والبدء في إرسالها للمعالجة بمجرد أن تكون كمية المعلومات كافية لتشغيل خوارزمية التصفية. أثناء قيام المخزن المؤقت بتجميع البيانات، سيعمل المرشح في وضع الخمول، دون استهلاك طاقة المعالجة للمعالج. ولكن بمجرد أن تقوم وظيفة القراءة من المخزن المؤقت بإرجاع قيمة غير الصفر، تبدأ وظيفة العملية () الخاصة بالمرشح في أخذ البيانات من المخزن المؤقت ومعالجتها في أجزاء بالحجم المطلوب، حتى يتم استنفادها.
تظل البيانات غير المطلوبة بعد في المخزن المؤقت باعتبارها العنصر الأول في المجموعة، والتي يتم إرفاق الكتل اللاحقة من بيانات الإدخال بها.

الهيكل الذي يصف المخزن المؤقت:

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

إذا كان حجم البيانات المتراكمة في المخزن المؤقت أقل من الحجم المطلوب (datalen)، ثم ترجع الدالة صفرًا، ولا يتم نسخ البيانات إلى بيانات. وبخلاف ذلك، يتم إجراء النسخ المتسلسل للبيانات من المجموعات الموجودة في المخزن المؤقت. بعد النسخ، يتم حذف الصف وتحرير الذاكرة. ينتهي النسخ في اللحظة التي يتم فيها نسخ بايتات البيانات. إذا نفدت المساحة في منتصف كتلة البيانات، ففي هذه الرسالة، سيتم تقليل كتلة البيانات إلى الجزء المتبقي غير المنسوخ. في المرة التالية التي تتصل فيها، سيستمر النسخ من هذه النقطة.

قراءة كمية البيانات المتوفرة حاليًا في المخزن المؤقت:

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

إضافة تعليق