کاوش در موتور VoIP Mediastreamer2. قسمت 11

مطالب مقاله از من گرفته شده است کانال ذن.

کاوش در موتور VoIP Mediastreamer2. قسمت 11

مکانیسم حرکت داده ها

  • بلوک داده dblk_t
  • mblk_t پیام دهید
  • توابع کار با پیام های mblk_t
  • صف صف_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

همانطور که گفته شد، عناصر صف از نوع هستند 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).

سپس اشاره گر می آید b_cont، که فقط زمانی استفاده می شود که پیام بخشی از یک تاپل باشد. برای آخرین پیام در تاپل، این اشاره گر خالی می ماند.

سپس یک اشاره گر به یک بلوک داده را می بینیم b_datap، که پیام برای آن وجود دارد. به دنبال آن نشانگرهایی به ناحیه داخل بافر داده بلوک وجود دارد. رشته b_rptr مکانی را مشخص می کند که داده های بافر از آنجا خوانده می شوند. رشته b_wptr مکانی را نشان می دهد که از آنجا نوشتن در بافر انجام می شود.

فیلدهای باقیمانده ماهیت خدماتی دارند و به عملکرد مکانیسم انتقال داده ارتباطی ندارند.

در زیر یک پیام واحد با نام وجود دارد m1 و بلوک داده d1.
کاوش در موتور VoIP Mediastreamer2. قسمت 11
شکل زیر مجموعه ای از سه پیام را نشان می دهد m1, m1_1, m1_2.
کاوش در موتور VoIP Mediastreamer2. قسمت 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_cont. در این صورت پیام به یک تاپل تبدیل می شود.

اگر می خواهید بلوک دیگری از داده را به tuple اضافه کنید، باید از تابع استفاده کنید:

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

او آخرین پیام را در تاپل (او دارد b_cont null خواهد بود) و تابع این پیام را فراخوانی می کند appendb().

می توانید اندازه داده ها را در یک پیام یا تاپل با استفاده از تابع پیدا کنید:

int msgdsize(const mblk_t *mp);

تمام پیام‌های تاپل را حلقه می‌کند و کل داده‌ها را در بافرهای داده آن پیام‌ها برمی‌گرداند. برای هر پیام، مقدار داده به صورت زیر محاسبه می شود:

 mp->b_wptr - mp->b_rptr

برای ترکیب دو تاپل، از تابع استفاده کنید:

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

او تاپل را اضافه می کند جدید تا دم تاپل mp و یک اشاره گر به آخرین پیغام تاپل به دست آمده برمی گرداند.

در صورت لزوم، یک تاپل را می توان با یک بلوک داده به یک پیام تبدیل کرد؛ این کار توسط تابع انجام می شود:

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

کپی کامل یک تاپل با تمام بلوک های داده:

mblk_t *copymsg(const mblk_t *mp);

عناصر تاپل توسط تابع کپی می شوند copyb().

یک کپی آسان از یک پیام ایجاد کنید mblk_t. در این حالت بلوک داده کپی نمی شود بلکه شمارنده مرجع آن افزایش می یابد db_ref:

mblk_t *dupb(mblk_t *mp);

ساخت یک کپی سبک از یک تاپل. بلوک های داده کپی نمی شوند، فقط شمارنده های مرجع آنها افزایش می یابد db_ref:

mblk_t *dupmsg(mblk_t* m);

چسباندن تمام پیام های یک تاپل در یک پیام:

void msgpullup(mblk_t *mp,size_t len);

اگر استدلال لن -1 است، سپس اندازه بافر اختصاص داده شده به طور خودکار تعیین می شود.

حذف یک پیام، تاپل:

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 را نشان می دهد.
کاوش در موتور VoIP Mediastreamer2. قسمت 11
شکل زیر یک صف به نام q1 حاوی 4 پیام m1,m2,m3,m4 را نشان می دهد. پیام m2 سر یک تاپل است که حاوی دو پیام دیگر m2_1 و m2_2 است.

کاوش در موتور VoIP Mediastreamer2. قسمت 11

توابع برای کار با صف queue_t

مقداردهی اولیه صف:

void qinit(queue_t *q);

رشته _q_stopper (از این پس آن را “stopper” می نامیم) توسط تابع مقداردهی اولیه می شود 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 (q, m) با اشاره گر تهی به پیام، تابع حلقه می شود. برنامه شما فریز می شود. مشابه رفتار می کند ms_queue_next(q, m).

اتصال فیلترها

صف توضیح داده شده در بالا برای ارسال پیام از یک فیلتر به فیلتر دیگر یا از یک به چندین فیلتر استفاده می شود. فیلترها و اتصالات آنها یک نمودار جهت دار را تشکیل می دهند. ورودی یا خروجی فیلتر را کلمه کلی "پین" می نامند. برای توصیف ترتیب اتصال فیلترها به یکدیگر، پخش کننده رسانه از مفهوم "نقطه سیگنال" استفاده می کند. نقطه سیگنال ساختار است _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 میلی ثانیه)، تیک تیک تابع را فراخوانی می کند. روند() برای تمام فیلترهای منبع که قبلاً پیدا شده اند، و سپس برای فیلترهای باقی مانده در لیست. اگر فیلتر دارای پیوندهای ورودی است، عملکرد را اجرا کنید روند() تا زمانی که صف های لینک ورودی خالی شوند تکرار می شود. پس از این، به فیلتر بعدی در لیست می رود و آن را "پیمایش" می کند تا پیوندهای ورودی بدون پیام باشند. تیک تیک از فیلتری به فیلتر دیگر حرکت می کند تا زمانی که لیست به پایان برسد. این پردازش چرخه را کامل می کند.

اکنون به تاپل ها برمی گردیم و در مورد اینکه چرا چنین موجودی به پخش کننده رسانه اضافه شده است صحبت خواهیم کرد. به طور کلی، مقدار داده مورد نیاز الگوریتمی که در داخل فیلتر کار می کند، منطبق نیست و مضربی از اندازه بافرهای داده دریافتی در ورودی نیست. به عنوان مثال، ما در حال نوشتن یک فیلتر هستیم که تبدیل فوریه سریع را انجام می دهد، که طبق تعریف فقط می تواند بلوک های داده ای را که اندازه آنها توان دو است پردازش کند. بگذارید 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); 

اگر اندازه داده های انباشته شده در بافر کمتر از مقدار درخواستی باشد (دادهلن، سپس تابع صفر را برمی گرداند، داده ها در داده ها کپی نمی شوند. در غیر این صورت، کپی متوالی داده ها از تاپل های واقع در بافر انجام می شود. پس از کپی، تاپل حذف شده و حافظه آزاد می شود. کپی در لحظه کپی شدن بایت های داده به پایان می رسد. اگر فضای وسط یک بلوک داده تمام شود، در این پیام، بلوک داده به قسمت کپی نشده باقیمانده کاهش می یابد. دفعه بعد که تماس گرفتید، کپی کردن از این نقطه ادامه خواهد داشت.

خواندن مقدار داده ای که در حال حاضر در بافر موجود است:

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

اضافه کردن نظر