Mediastreamer2 VoIP انجڻ جي ڳولا. حصو 10

مضمون جو مواد منهنجي طرفان ورتو ويو آهي زين چينل.

آخري ۾ مضمون اسان هڪ ڊپلڪس انٽرڪام ٺاهيو جيڪو ڊيپلڪس آر ٽي پي سيشن ذريعي آڊيو سگنل مٽائي ٿو. هن آرٽيڪل ۾، اسان سکنداسين ته فلٽر ڪيئن لکجي ۽ هڪ DIY انٽرڪام ۾ هڪ DIY فلٽر شامل ڪجي.

اسان هڪ پلگ ان ٺاهي رهيا آهيون

Mediastreamer2 VoIP انجڻ جي ڳولا. حصو 10

ميڊيا اسٽريمر ۾ پلگ ان، جيئن ٻين ڪيترن ئي پروگرامن ۾، استعمال ڪيا ويندا آهن ڪارڪردگي کي وڌائڻ لاءِ بغير ميڊيا اسٽريمر کي پاڻ کي ٻيهر ٺاهڻ جي.

توھان جي پروگرام ۾ پلگ ان استعمال ڪرڻ لاء، توھان استعمال ڪريو شامل آهن پلگ ان هيڊر فائل شامل ڪرڻ لازمي آهي. پروگرام جي جسم ۾، فنڪشن y استعمال ڪندي ms_filter_register() نئون فلٽر رجسٽر ڪريو. قدرتي طور تي، توهان جو پروگرام ۽ پلگ ان جو ذريعو لازمي طور تي گڏ ڪيو وڃي ۽ هڪ ايپليڪيشن ۾ گڏ ڪيو وڃي.

ھاڻي اچو ھڪ پلگ ان لکڻ ڏانھن. سڀئي ميڊيا اسٽريمر فلٽر ۽ پلگ ان انهن جي لکڻين ۾ هڪ عام ڪينن جي پيروي ڪندا آهن، جيڪو توهان کي پڙهڻ چاهيو ٿا ته ايندڙ فلٽر جي جوڙجڪ کي سمجهڻ آسان بڻائي ٿي. تنهن ڪري، وڌيڪ، ادارن کي ضرب نه ڏيڻ لاء، آئون پلگ ان فلٽر کي سڏيندس.

اچو ته چئو ته اسان NASH_FILTR نالي هڪ نئون فلٽر ٺاهڻ چاهيون ٿا. اھو ھڪڙو سادو ڪم ڪندو - ان جي ھڪڙي ان پٽ مان بلاڪ وصول ڪريو ۽ ان کي پنھنجي پنجن ٻاھرين ڏانھن منتقل ڪريو. اهو پڻ هڪ واقعو پيدا ڪندو جيڪڏهن پنجن کان وڌيڪ بلاڪ هڪ ڏنل حد کان هيٺ سگنل جي سطح سان ان جي ذريعي گذري ٿو، ۽ جيڪڏهن پنجن کان وڌيڪ بلاڪ هڪ سگنل جي سطح سان گڏ حد کان مٿي گذري ٿو، اهو پڻ هڪ واقعو پيدا ڪندو.

حد مقرر ڪئي ويندي فلٽر طريقو استعمال ڪندي. ٻئي ۽ ٽيون طريقا اجازت ڏين ٿا/منع ڪن ٿا بلاڪ جي پاسن کي نڪرڻ لاءِ.

اچو ته شروع ڪريون. جڏهن فلٽر لکڻ، توهان کي هيڊر فائل سان شروع ڪرڻ جي ضرورت آهي. پهرين لائنن ۾ ان کي فائل شامل ڪرڻ گهرجي msfilter.h, MS_FILTER_METHOD ميڪرو استعمال ڪندي، نئين فلٽر جي طريقن جو اعلان ڪريو (جيڪڏھن ڪو ھجي)، فلٽر پاران پيدا ڪيل واقعن جو اعلان ڪريو (جيڪڏھن ڪو آھي) ۽ قسم جي برآمد ٿيل ساخت جو اعلان ڪريو MSFilterDesc فلٽر جي ماپ جي وضاحت سان:

/* Файл nash_filter.h, описывает фильтр-разветвитель и нойзгейт. */

#ifndef myfilter_h
#define myfilter_h

/* Подключаем заголовочный файл с перечислением фильтров медиастримера. */
#include <mediastreamer2/msticker.h>

/* 
   Задаем числовой идентификатор нового типа фильтра.  Это число не должно
   совпадать ни с одним из других типов.  В медиастримере  в файле allfilters.h
   есть соответствующее перечисление enum MSFilterId. К сожалению, непонятно
   как определить максимальное занятое значение, кроме как заглянуть в этот
   файл. Но мы возьмем в качестве id для нашего фильтра заведомо большее
   значение: 4000.  Будем полагать, что разработчики добавляя новые фильтры, не
   скоро доберутся до этого номера.  
   */
#define NASH_FILTER_ID 4000

/* 
   Определяем методы нашего фильтра. Вторым параметром макроса должен
   порядковый номер метода, число от 0.  Третий параметр это тип аргумента
   метода, указатель на который будет передаваться методу при вызове. У методов
   аргументов может и не быть, как показано ниже. 
   */
#define NASH_FILTER_SET_TRESHOLD MS_FILTER_METHOD(NASH_FILTER_ID , 0, float)
#define NASH_FILTER_TUNE_OFF     MS_FILTER_METHOD_NO_ARG(NASH_FILTER_ID ,1)
#define NASH_FILTER_TUNE_ON      MS_FILTER_METHOD_NO_ARG(NASH_FILTER_ID ,2)

/* Теперь определяем структуру, которая будет передаваться вместе с событием. */
struct _NASHFilterEvent
{
    /* Это поле, которое будет выполнять роль флага,
       0 - появились нули, 1 - появился сигнал.*/
    char state; 
    /* Время, когда произошло событие. */
    uint64_t time;
};
typedef struct _NASHFilterEvent NASHFilterEvent;

/* Определяем событие для нашего фильтра. */
#define NASH_FILTER_EVENT MS_FILTER_EVENT(MS_RTP_RECV_ID, 0, NASHFilterEvent)

/* Определяем экспортируемую переменную, которая будет
   хранить характеристики для данного типа фильтров. */
extern MSFilterDesc nash_filter_desc;

#endif /* myfilter_h */

ھاڻي توھان منتقل ڪري سگھوٿا ماخذ فائل ڏانھن. تبصرن سان فلٽر لاءِ ماخذ ڪوڊ هيٺ ڏيکاريل آهي. فلٽر جا طريقا ۽ گهربل فلٽر افعال هتي بيان ڪيا ويا آهن. پوء طريقن ۽ افعال جا حوالا برآمد ٿيل ڍانچي ۾ ھڪڙي خاص ترتيب ۾ رکيل آھن اسان جو_فلٽر_ڊيسڪ. جيڪو ميڊيا اسٽريمر طرفان استعمال ڪيو ويندو آهي ”امپلانٽ“ فلٽر هن قسم جي ڊيٽا پروسيسنگ ورڪ فلو ۾.

/* Файл nash_filter.с, описывает фильтр-разветвитель и нойзгейт. */

#include "nash_filter.h"
#include <math.h>

#define NASH_FILTER_NOUTPUTS 5

/* Определяем структуру, которая хранит внутреннее состояние фильтра. */
typedef struct _nash_filterData
{
    bool_t disable_out;  /* Разрешение передачи блоков на выход. */
    int last_state;   /* Текущее состояние переключателя. */
    char zero_count;     /* Счетчик нулевых блоков. */
    char lag;            /* Количество блоков для принятия решения нойзгейтом. */
    char n_count;        /* Счетчик НЕнулевых блоков. */
    float skz_level;     /* Среднеквадратическое значение сигнала внутри
блока, при котором фильтр будет пропускать сигнал. Одновременно это порог
срабатывания, по которому будет формироваться событие.  */

} nash_filterData;

/*----------------------------------------------------------*/
/* Обязательная функция инициализации. */
static void nash_filter_init(MSFilter *f)
{
    nash_filterData *d=ms_new0(nash_filterData, 1);
    d->lag=5;
    f->data=d;
}

/*----------------------------------------------------------*/
/* Обязательная функция финализации работы фильтра,
   освобождается память. */
static void nash_filter_uninit(MSFilter *f)
{
    ms_free(f->data);
}

/*----------------------------------------------------------*/
/* Определяем образцовый массив с нулями, заведомо
   большего размера чем блок. */
char zero_array[1024]={0};

/* Определяем событие фильтра. */
NASHFilterEvent event;

/*----------------------------------------------------------*/
/* Функция отправки события. */
static void send_event(MSFilter *f, int state)
{
    nash_filterData *d =( nash_filterData* ) f->data;
     d->last_state = state;
    /* Устанавливаем время возникновения события,
       от момента первого тика. Время в миллисекундах. */
    event.time=f -> ticker -> time;
    event.state=state;  
    ms_filter_notify(f, NASH_FILTER_EVENT, &event);
}   

/*----------------------------------------------------------*/
/* Функция вычисляет среднеквадратическое (эффективное) значение сигнала внутри
  блока. */
static float calc_skz(nash_filterData *d, int16_t *signal, int numsamples)
{
    int i;
    float acc = 0;
    for (i=0; i<numsamples; i++)
    {
        int s=signal[i];
        acc = acc + s * s;
    }
    float skz = (float)sqrt(acc / numsamples);
    return skz;
}

/*----------------------------------------------------------*/
/* Обязательная функция основного цикла фильтра,
   вызывается с каждым тиком. */
static void nash_filter_process(MSFilter *f)
{
    nash_filterData *d=(nash_filterData*)f->data;

    /* Указатель на входное сообщение содержащее блок данных. */
    mblk_t *im;
    int i;
    int state;
    /* Вычитываем сообщения из входной очереди
       до полного её опустошения. */
    while((im=ms_queue_get(f->inputs[0]))!=NULL)
    {
        /* Если выходы запрещены, то просто удаляем входное сообщение. */
        if ( d -> disable_out)
        {
          freemsg(im);
          continue;
        }

        /* Измеряем уровень сигнала и принимаем решение об отправке сигнала. */
        float skz = calc_skz(d, (int16_t*)im->b_rptr, msgdsize(im));
        state = (skz > d->skz_level) ? 1 : 0; 
        if (state) 
        {
            d->n_count++;
            d->zero_count = 0;
        }
        else
        {
            d->n_count = 0;
            d->zero_count++;
        }
        if (((d->zero_count > d->lag) || (d->n_count > d->lag))
            &&  (d->last_state != state)) send_event(f, state);

        /* Приступаем к копированию входного сообщения и раскладке по выходам. Но
         * только по тем, к которым подключена нагрузка. Оригинальное сообщение
         * уйдет на выход с индексом 0, а его копии попадут на остальные
         * выходы. */ 
        int output_count = 0;
        mblk_t *outm; /* Указатель на сообщение с выходным блоком данных. */
        for(i=0; i < f->desc->noutputs; i++)
        {
            if (f->outputs[i]!=NULL)
            {
                if (output_count == 0)
                {
                    outm = im;
                }
                else
                {
                    /* Создаем легкую копию сообщения. */       
                    outm = dupmsg(im);
                }
                /* Помещаем копию или оригинал входного сообщения на очередной
                 * выход фильтра. */ 
                ms_queue_put(f->outputs[i], outm);
                output_count++;
            }
        }
    }
}

/*----------------------------------------------------------*/
/* Функция-обработчик вызова метода NASH_FILTER_SET_LAG. */
static int nash_filter_set_treshold(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->skz_level=*(float*)arg;
    return 0;
}

/*----------------------------------------------------------*/
/* Функция-обработчик вызова метода NASH_FILTER_TUNE_OFF. */
static int nash_filter_tune_off(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->disable_out=TRUE;
    return 0;
}

/*----------------------------------------------------------*/
/* Функция-обработчик вызова метода NASH_FILTER_TUNE_ON. */
static int nash_filter_tune_on(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->disable_out=FALSE;
    return 0;
}

/*----------------------------------------------------------*/
/* Заполняем таблицу методов фильтра, сколько методов
   мы определили в заголовочном файле столько ненулевых
   строк. */
static MSFilterMethod nash_filter_methods[]={
    { NASH_FILTER_SET_TRESHOLD, nash_filter_set_treshold },
    { NASH_FILTER_TUNE_OFF, nash_filter_tune_off },
    { NASH_FILTER_TUNE_ON, nash_filter_tune_on },
    { 0 , NULL } /* Маркер конца таблицы. */
};

/*----------------------------------------------------------*/
/* Описание фильтра для медиастримера. */
MSFilterDesc nash_filter_desc=
{
    NASH_FILTER_ID,
    "NASH_FILTER",
    "A filter with noise gate that reads from input and copy to it's five outputs.",
    MS_FILTER_OTHER,
    NULL,
    1,
    NASH_FILTER_NOUTPUTS,
    nash_filter_init,
    NULL,
    nash_filter_process,
    NULL,
    nash_filter_uninit,
    nash_filter_methods
};

MS_FILTER_DESC_EXPORT(nash_filter_desc)

ھاڻي، بغير دير جي، اچو ته اسان جو فلٽر استعمال ڪريون انٽرڪام ۾ جيڪو اسان اڳ ۾ ٺاھيو آھي. عنوان جي تصوير هڪ تبديل ٿيل انٽرڪام جو هڪ ڊراگرام ڏيکاري ٿو.
اسان چاهيون ٿا ته اسان جي هٿ سان ٺهيل فلٽر کي خاص طور تي روشن انداز ۾ ظاهر ڪيو وڃي. تنهن ڪري، توهان فوري طور تي اسان جو فلٽر ڊاگرام ۾ ڳوليندا.

هڪ فلٽر رڪارڊر سرڪٽ ۾ شامل ڪيو ويو آهي، جيڪو لکندو آهي ان پٽ سگنل هڪ wav فائل ڏانهن. جيئن منصوبابندي ڪئي وئي، اسان جو فلٽر توهان کي اجازت ڏيندو ته توهان فائل ۾ تقرير ۾ رڪاوٽون لکڻ کان پاسو ڪندا. اهڙيء طرح ان جي سائيز کي گھٽائڻ.
مضمون جي شروعات ۾، اسان فلٽر جي الگورتھم کي بيان ڪيو. مکيه ايپليڪيشن انهن واقعن کي سنڀاليندو آهي جيڪو اهو ٺاهي ٿو. جيڪڏهن واقعي "0" پرچم تي مشتمل آهي، پوء ميزبان ايپليڪيشن رڪارڊنگ کي روڪي ٿو. جيترو جلدي هڪ واقعو پرچم سان "1" اچي ٿو، رڪارڊنگ ٻيهر شروع ٿئي ٿو.

ٻن وڌيڪ ڪمانڊ لائين دليلن کي شامل ڪيو ويو آھي پوئين وارن ۾: --ng، جيڪو فلٽر جي حد مقرر ڪري ٿو سطح ۽ --recجيڪو هڪ فائل ڏانهن لکڻ شروع ڪري ٿو رڪارڊ.wav.

/* Файл mstest9.c Имитатор переговорного устройства c регистратором и
* нойзгейтом. */
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>
#include <mediastreamer2/msfilerec.h>
/* Подключаем наш фильтр. */
#include "nash_filter.h"
/* Подключаем файл общих функций. */
#include "mstest_common.c"
/*----------------------------------------------------------*/
struct _app_vars
{
int  local_port;              /* Локальный порт. */
int  remote_port;             /* Порт переговорного устройства на удаленном компьютере. */
char remote_addr[128];        /* IP-адрес удаленного компьютера. */
MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
MSFilter* recorder;           /* Указатель на фильтр регистратор. */
bool_t file_is_open;          /* Флаг того, что файл для записи открыт. */
/* Порог, при котором прекращается запись принимаемого сигнала в файл. */
float treshold; 
bool_t en_rec;                /*Включить запись в файл.*/    
};
typedef struct _app_vars app_vars;
/*----------------------------------------------------------*/
/* Создаем дуплексную RTP-сессию. */
RtpSession* create_duplex_rtp_session(app_vars v)
{
RtpSession *session = create_rtpsession (v.local_port, v.local_port + 1,
FALSE, RTP_SESSION_SENDRECV);
rtp_session_set_remote_addr_and_port(session, v.remote_addr, v.remote_port,
v.remote_port + 1);
rtp_session_set_send_payload_type(session, PCMU);
return session;
}
/*----------------------------------------------------------*/
/* Функция преобразования аргументов командной строки в 
* настройки программы. */
void  scan_args(int argc, char *argv[], app_vars *v)
{
char i;
for (i=0; i<argc; i++)
{
if (!strcmp(argv[i], "--help"))
{
char *p=argv[0]; p=p + 2;
printf("  %s walkie talkienn", p);
printf("--help      List of options.n");
printf("--version   Version of application.n");
printf("--addr      Remote abonent IP address string.n");
printf("--port      Remote abonent port number.n");
printf("--lport     Local port number.n");
printf("--gen       Generator frequency.n");
printf("--ng        Noise gate treshold level from 0. to 1.0n");
printf("--rec       record to file 'record.wav'.n");
exit(0);
}
if (!strcmp(argv[i], "--version"))
{
printf("0.1n");
exit(0);
}
if (!strcmp(argv[i], "--addr"))
{
strncpy(v->remote_addr, argv[i+1], 16);
v->remote_addr[16]=0;
printf("remote addr: %sn", v->remote_addr);
}
if (!strcmp(argv[i], "--port"))
{
v->remote_port=atoi(argv[i+1]);
printf("remote port: %in", v->remote_port);
}
if (!strcmp(argv[i], "--lport"))
{
v->local_port=atoi(argv[i+1]);
printf("local port : %in", v->local_port);
}
if (!strcmp(argv[i], "--gen"))
{
v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
printf("gen freq : %in", v -> dtmf_cfg.frequencies[0]);
}
if (!strcmp(argv[i], "--ng"))
{
v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
printf("noise gate treshold: %fn", v -> treshold);
}
if (!strcmp(argv[i], "--rec"))
{
v -> en_rec = TRUE;
printf("enable recording: %in", v -> en_rec);
}
}
}
/*----------------------------------------------------------*/
/* Функция обратного вызова, она будет вызвана фильтром, как только он
* заметит, что наступила тишина или наоборот тишина сменилась звуками. */
static void change_detected_cb(void *data, MSFilter *f, unsigned int event_id,
NASHFilterEvent *ev)
{
app_vars *vars = (app_vars*) data;
/* Если запись не была разрешена, то выходим. */
if (! vars -> en_rec) return; 
if (ev -> state)
{
/* Возобновляем запись. */
if(!vars->file_is_open)
{
ms_filter_call_method(vars->recorder, MS_FILE_REC_OPEN, "record.wav");
vars->file_is_open = 1;
}
ms_filter_call_method(vars->recorder, MS_FILE_REC_START, 0);
printf("Recording...n");
}
else
{
/* Приостанавливаем запись. */
ms_filter_call_method(vars->recorder, MS_FILE_REC_STOP, 0);
printf("Pause...n");
}
}
/*----------------------------------------------------------*/
int main(int argc, char *argv[])
{
/* Устанавливаем настройки по умолчанию. */
app_vars vars={5004, 7010, "127.0.0.1", {0}, 0, 0, 0.01, 0};
/* Устанавливаем настройки настройки программы в 
* соответствии с аргументами командной строки. */
scan_args(argc, argv, &vars);
ms_init();
/* Создаем экземпляры фильтров передающего тракта. */
MSSndCard *snd_card =
ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
MSFilter *snd_card_read = ms_snd_card_create_reader(snd_card);
MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID);
/* Создаем фильтр кодера. */
MSFilter *encoder = ms_filter_create_encoder("PCMU");
/* Регистрируем типы нагрузки. */
register_payloads();
/* Создаем дуплексную RTP-сессию. */
RtpSession* rtp_session = create_duplex_rtp_session(vars);
ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, rtp_session);
/* Соединяем фильтры передатчика. */
ms_filter_link(snd_card_read, 0, dtmfgen, 0);
ms_filter_link(dtmfgen, 0, encoder, 0);
ms_filter_link(encoder, 0, rtpsend, 0);
/* Создаем фильтры приемного тракта. */
MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rtp_session);
/* Создаем фильтр декодера. */
MSFilter *decoder=ms_filter_create_decoder("PCMU");
//MS_FILE_REC_ID
/* Регистрируем наш фильтр. */
ms_filter_register(&nash_filter_desc);
MSFilter *nash = ms_filter_new(NASH_FILTER_ID);
/* Создаем фильтр звуковой карты. */
MSFilter *snd_card_write = ms_snd_card_create_writer(snd_card);
/* Создаем фильтр регистратора. */
MSFilter *recorder=ms_filter_new(MS_FILE_REC_ID);
vars.recorder = recorder; 
/* Соединяем фильтры приёмного тракта. */
ms_filter_link(rtprecv, 0, decoder, 0);
ms_filter_link(decoder, 0, nash, 0);
ms_filter_link(nash, 0, snd_card_write, 0);
ms_filter_link(nash, 1, recorder, 0);
/* Подключаем к фильтру функцию обратного вызова, и передаем ей в
* качестве пользовательских данных указатель на структуру с настройками
* программы, в которой среди прочих есть указать на фильтр
* регистратора. */
ms_filter_set_notify_callback(nash,
(MSFilterNotifyFunc)change_detected_cb, &vars);
ms_filter_call_method(nash,NASH_FILTER_SET_TRESHOLD, &vars.treshold); 
/* Создаем источник тактов - тикер. */
MSTicker *ticker = ms_ticker_new();
/* Подключаем источник тактов. */
ms_ticker_attach(ticker, snd_card_read);
ms_ticker_attach(ticker, rtprecv);
/* Если настройка частоты генератора отлична от нуля, то запускаем генератор. */   
if (vars.dtmf_cfg.frequencies[0])
{
/* Настраиваем структуру, управляющую выходным сигналом генератора. */
vars.dtmf_cfg.duration = 10000;
vars.dtmf_cfg.amplitude = 1.0;
}
/* Организуем цикл перезапуска генератора. */
printf("Press ENTER to exit.n ");
char c=getchar();
while(c != 'n')
{
if(vars.dtmf_cfg.frequencies[0])
{
/* Включаем звуковой генератор. */
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
(void*)&vars.dtmf_cfg);
}
char c=getchar();
printf("--n");
}
if (vars.en_rec ) ms_filter_call_method(recorder, MS_FILE_REC_CLOSE, 0);
}

حقيقت اها آهي ته اسان فائلون شامل ڪيون ۽ لائبريري استعمال ڪيو رياضي، تاليف لاءِ ڪمانڊ لائن وڌيڪ پيچيده ٿي چڪي آهي، ۽ هن وانگر ڏسڻ ۾ اچي ٿي:

$ gcc mstest9.c nash_filter.c -o mstest9   `pkg-config mediastreamer   --libs --cflags`  -lm

ايپليڪيشن ٺاھڻ کان پوء، ھيٺ ڏنل دليلن سان ان کي پھرين ڪمپيوٽر تي ھلايو:

$ ./mstest9  --lport 7010  --port 8010 --addr <тут адрес второго компьютера> --rec

ٻئي ڪمپيوٽر تي اسان ھيٺ ڏنل سيٽنگن سان لانچ ڪنداسين:

$ ./mstest9  --lport 8010  --port 7010 --addr <тут адрес первого компьютера>

ان کان پوء، پهريون ڪمپيوٽر هر شيء کي رڪارڊ ڪرڻ شروع ڪندو جيڪو توهان چوندا آهيو سيڪنڊ جي مائڪروفون ۾. هن معاملي ۾، لفظ "رڪارڊنگ…"جيئن ئي توهان خاموش ٿي ويندا، رڪارڊنگ کي روڪيو ويندو هڪ پيغام سان ڏيکاريل"روڪيو ..."توهان کي شايد حد جي سطح سان تجربو ڪرڻ جي ضرورت پوندي.

هن آرٽيڪل ۾ اسان سکيو ته فلٽر ڪيئن لکجي. جئين توهان محسوس ڪيو هوندو، nash_filter_process() فنڪشن ڊيٽا بلاڪ سان ٺهڪندڙ انجام ڏئي ٿو. جيئن ته مثال تعليمي آهي، ميڊيا اسٽريمر جي گھٽ ۾ گھٽ صلاحيتون ڊيٽا بلاڪ کي هٿي ڏيڻ لاء استعمال ڪيا ويا.

اڳيان مضمون اسان پيغام جي قطار ۽ پيغام جي انتظام جي افعال تي نظر ڪنداسين. اهو مستقبل ۾ وڌيڪ پيچيده معلومات پروسيسنگ سان فلٽر ٺاهڻ ۾ مدد ڪندو.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو