Ho hlahloba enjene ea Mediastreamer2 VoIP. Karolo ea 10

Thepa ea sehlooho e nkiloe ho ea ka mocha oa zen.

Qetellong sehlooho re entse duplex intercom e fapanyetsanang matshwao a medumo ka duplex RTP seshene. Sehloohong sena, re tla ithuta ho ngola li-filters le ho eketsa filthara ea DIY ho intercom ea DIY.

Re ntse re etsa plugin

Ho hlahloba enjene ea Mediastreamer2 VoIP. Karolo ea 10

Li-plugins tsa media streamer, joalo ka mananeong a mang a mangata, li sebelisetsoa ho holisa ts'ebetso ntle le tlhoko ea ho bokella media streamer ka boeona.

Ho sebelisa plugin lenaneong la hau, u sebelisa kenyeletsa e tlameha ho kenyelletsa faele ea hlooho ea plugin. 'Meleng oa lenaneo, ho sebelisa mosebetsi y ms_filter_register() ngolisa sefe e ncha. Ka tlhaho, lenaneo la hau le mohloli oa plugin li tlameha ho bokelloa le ho bokelloa ka kopo e le 'ngoe.

Joale ha re bueng ka ho ngola plugin. Li-filters tsohle tsa media streamer le li-plugins li latela kanone e tloaelehileng ha li ngola, e leng se etsang hore ho be bonolo haholo ho utloisisa sebopeho sa sefe se latelang seo u batlang ho ithuta sona. Ka hona, ho ea pele, e le hore ke se ke ka atisa mekhatlo, ke tla bitsa li-filters tsa plugins.

Ha re re re batla ho hlahisa sefe se secha se bitsoang NASH_FILTR. E tla etsa ntho e bonolo - e amohela li-blocks ho tsoa ho kenyelletso ea eona e le 'ngoe ebe e e fetisetsa ho liphetho tsa eona tse hlano. E tla boela e hlahise ketsahalo haeba li-blocks tse fetang tse hlano tse nang le tekanyo ea matšoao ka tlase ho moeli o fanoeng li feta ho eona, 'me haeba li-blocks tse fetang tse hlano tse nang le boemo ba pontšo ka holim'a monyako li feta ho eona, li tla boela li hlahise ketsahalo.

Mohaho o tla behoa ho sebelisoa mokhoa oa ho hloekisa. Mokhoa oa bobeli le oa boraro o tla lumella / thibela ho feta ha li-blocks ho ea ho tsoa.

Ha re qaleng. Ha o ngola filthara, o hloka ho qala ka faele ea hlooho. Meleng ea pele e lokela ho kenyelletsa faele msfilter.h, u sebelisa MS_FILTER_METHOD macro, phatlalatsa mekhoa ea sefe e ncha (haeba e teng), phatlalatsa liketsahalo tse hlahisoang ke sefe (haeba li teng) 'me u phatlalatse sebopeho se rometsoeng sa mofuta oo. MSFilterDesc ka tlhaloso ea liparamente tsa filthara:

/* Файл 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 */

Hona joale o ka fetela ho file ea mohloli. Khoutu ea mohloli oa sefe e nang le maikutlo e bontšitsoe ka tlase. Mekhoa ea ho sefa le mesebetsi e hlokahalang ea ho sefa e hlalositsoe mona. Joale litšupiso tsa mekhoa le mesebetsi li behoa ka tatellano e itseng sebopehong se rometsoeng kantle ho naha Our_filter_desc. E sebelisoang ke media streamer ho "kenya" li-filters tsa mofuta ona ts'ebetsong ea ts'ebetso ea data.

/* Файл 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)

Joale, ntle le tieho, ha re sebeliseng sefe ea rona ho inthak'homo eo re e entseng pejana. Setšoantšo sa sehlooho se bontša setšoantšo sa intercom e fetotsoeng.
Re ne re batla ho hlahisa sefe ea rona e entsoeng ka letsoho ka tsela e khanyang haholo. Ka hona, hang-hang u tla fumana filthara ea rona setšoantšong.

Sehatisi se sefang se kentsoe potolohong, se ngolang lets'oao la ho kenya faeleng ea wav. Joalo ka ha ho reriloe, sefa sa rona se tla u lumella ho qoba ho ngola khefutso ea puo faeleng. Kahoo ho fokotsa boholo ba eona.
Qalong ea sengoloa, re hlalositse algorithm ea filthara. Sesebelisoa se seholo se sebetsana le liketsahalo tseo e li hlahisang. Haeba ketsahalo e na le folakha ea "0", joale sesebelisoa sa moamoheli se emisa ho rekota. Hang ha ketsahalo e nang le folakha "1" e fihla, ho rekota ho qala hape.

Likhang tse ling tse peli tsa mola oa taelo li kentsoe ho tse fetileng: --ng, e behang moeli oa filthara le --rece qalang ho ngolla faele e bitsoang rekoto.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);
}

Ka lebaka la hore re kentse lifaele mme ra sebelisa laebrari lipalo, mola oa taelo bakeng sa ho bokella o se o rarahane le ho feta, 'me o shebahala tjena:

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

Kamora ho theha ts'ebeliso, e tsamaise komporong ea pele ka lintlha tse latelang:

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

Ka komporong ea bobeli re qala ka li-setting tse latelang:

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

Kamora sena, komporo ea pele e tla qala ho hatisa ntho e 'ngoe le e' ngoe eo u e buang ho microphone ea bobeli. Tabeng ena, lentsoe "E ea rekota...". Hang ha u khutsa, ho rekota ho tla emisoa ka molaetsa o hlahang"Khefutsa…"Ho ka 'na ha hlokahala hore u leke boemo ba threshold.

Sehloohong sena re ithutile mokhoa oa ho ngola li-filters. Joalokaha e ka 'na eaba u hlokometse, mosebetsi oa nash_filter_process () o etsa bolotsana ka li-blocks tsa data. Kaha mohlala ke oa thuto, ho ile ha sebelisoa bokhoni bo fokolang ba media streamer bakeng sa ho laola li-block tsa data.

E 'ngoe sehlooho Re tla sheba tatellano ea melaetsa le mesebetsi ea taolo ea melaetsa. Sena se tla thusa nakong e tlang ho etsa li-filters tse nang le ts'ebetso e rarahaneng ea tlhaiso-leseling.

Source: www.habr.com

Eketsa ka tlhaloso