Vekolîna motora VoIP ya Mediastreamer2. Beş 9

Madeya gotarê ji min hatiye girtin kanala zen.

Duplex intercom

Vekolîna motora VoIP ya Mediastreamer2. Beş 9

Di ya paşîn de gotara intercomek duplex hate ragihandin, û di vê yekê de em ê çêbikin.

Diagram di jimareya sernavê de tê nîşandan. Zincîra jêrîn a parzûnan riya veguheztinê pêk tîne, ku ji qerta deng dest pê dike. Ew nimûneyên sînyalê ji mîkrofonê peyda dike. Ji hêla xwerû, ev bi rêjeya 8000 nimûne di çirkeyê de pêk tê. Kûrahiya bîta daneyê ya ku fîlterên bihîstwerî yên medyayê bikar tînin 16 bit e (ev ne girîng e; heke hûn bixwazin, hûn dikarin fîlteran binivîsin ku dê bi kûrahiyek bitek mezintir bixebitin). Daneyên di blokên 160 nimûneyan de têne kom kirin. Bi vî rengî, her blok bi mezinahiya 320 byte ye. Dûv re, em daneyan digihînin têketina jeneratorê, ku dema ku were girtin, ji daneyan re "şefaf" e. Min ew lê zêde kir heke hûn di dema debugkirinê de ji axaftina mîkrofonê westi bin - hûn dikarin jeneratorê bikar bînin da ku bi îşaretek tone rêyê "bişkînin".

Piştî jeneratorê, sînyala diçe şîfrekerê, ku nimûneyên me yên 16-bit li gorî μ-qanûnê (standard G.711) diguhezîne yên heşt-bit. Di derketina şîfrekerê de, me berê bloka daneyê ya nîvê mezinahiyê heye. Bi gelemperî, heke em ne hewce ne ku seyrûseferê xilas bikin, em dikarin daneyan bêyî berhevkirinê bişînin. Lê li vir karanîna şîfrekerek kêrhatî ye, ji ber ku Wireshark tenê dema ku ew li gorî μ-qanûn an zagonek were berhev kirin dikare dengek ji stûnek RTP-ê dubare bike.

Piştî şîfrekerê, blokên siviktir ên daneyê ji parzûna rtpsend re têne şandin, ku ew ê wan bixe nav pakêtek RTP, alayên pêwîst saz bike û wan bide stêrka medyayê da ku di forma pakêtek UDP de li ser torê veguhezîne.

Zincîra jorîn a parzûnan riya wergirtinê pêk tîne; Pakêtên RTP yên ku ji torê vekêşana medyayê têne wergirtin, dikevin parzûna rtprecv, di derana wê de ew di forma blokên daneyê de xuya dikin, ku her yek ji pakêtek wergirtî re têkildar e. Blok tenê daneyên bargiraniyê dihewîne; di gotara berê de ew di nîgarê de bi kesk hatine destnîşan kirin.

Dûv re, blokan ji parzûna dekoderê re têne şandin, ku nimûneyên yek-byte yên ku di wan de hene vediguhezîne yên xêzkirî, 16-bit. Ya ku jixwe dikare ji hêla fîlterên weşana medyayê ve were pêvajo kirin. Di doza me de, em tenê wan ji bo lîstina li ser axaftvanên guhê we dişînin qerta deng.

Naha em werin ser pêkanîna nermalavê. Ji bo vê yekê, em ê pelên wergir û veguhêz ên ku me berê ji hev veqetandine hev bikin. Beriya vê, me mîhengên sabît ji bo port û navnîşan bikar anîn, lê naha ji me re hewce ye ku bername bikaribe mîhengên ku em di destpêkê de destnîşan dikin bikar bînin. Ji bo vê yekê, em ê fonksiyonê ji bo hilberandina argumanên rêzika fermanê zêde bikin. Piştî vê yekê em ê bikaribin navnîşana IP-yê û porta interkomê ya ku em dixwazin pêwendiyek pê re saz bikin saz bikin.

Pêşîn, em strukturek li bernameyê zêde bikin ku dê mîhengên wê hilîne:

struct _app_vars
{
  int  local_port;              /* Локальный порт. */
  int  remote_port;             /* Порт переговорного устройства на удаленном компьютере. */
  char remote_addr[128];        /* IP-адрес удаленного компьютера. */
  MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};

typedef struct _app_vars app_vars;

Bername dê avahiyek bi vî rengî ya bi navê vars ragihîne.
Dûv re, em fonksiyonek lê zêde bikin ku argumanên rêza fermanê pars bike:

/* Функция преобразования аргументов командной строки в
* настройки программы. */
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");
            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]);
        }
    }
}

Di encama parskirinê de, argûmanên rêzika fermanê dê li qadên avahiya varsê werin danîn. Fonksiyona sereke ya serîlêdanê dê berhevkirina rêyên veguheztin û wergirtinê ji parzûnan be; piştî girêdana tîkerê, kontrol dê veguhezîne xelekek bêdawî ku, heke frekansa jeneratorê li ser ne-sifir were danîn, dê jeneratorê ceribandinê ji nû ve bide destpêkirin. ew bê rawestan dixebite.

Ji ber sêwirana xwe jenerator dê hewceyê van ji nû ve destpêkirinê bike; ji ber hin sedeman ew nikare sînyalek ji 16 çirkeyan zêdetir bidomîne. Divê were zanîn ku dema wê ji hêla hejmarek 32-bit ve tête diyar kirin.

Tevahiya bernameyê dê wiha xuya bike:

/* Файл mstest8.c Имитатор переговорного устройства. */

#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>

/* Подключаем файл общих функций. */
#include "mstest_common.c"

/*----------------------------------------------------------*/
struct _app_vars
{
    int  local_port;              /* Локальный порт. */
    int  remote_port;             /* Порт переговорного устройства на удаленном компьютере. */
    char remote_addr[128];        /* IP-адрес удаленного компьютера. */
    MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};

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");
            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]);
        }
    }
}

/*----------------------------------------------------------*/
int main(int argc, char *argv[])
{
    /* Устанавливаем настройки по умолчанию. */
    app_vars vars={5004, 7010, "127.0.0.1", {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");

    /* Создаем фильтр звуковой карты. */
    MSFilter *snd_card_write = ms_snd_card_create_writer(snd_card);

    /* Соединяем фильтры приёмного тракта. */
    ms_filter_link(rtprecv, 0, decoder, 0);
    ms_filter_link(decoder, 0,  snd_card_write, 0);

    /* Создаем источник тактов - тикер. */
    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;
    }

    /* Организуем цикл перезапуска генератора. */
    while(TRUE)
    {
        if(vars.dtmf_cfg.frequencies[0])
        {
            /* Включаем звуковой генератор. */
            ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
                    (void*)&vars.dtmf_cfg);
        }
        /* Укладываем тред в спячку на 20мс, чтобы другие треды
         * приложения получили время на работу. */
        ms_usleep(20000);
    }
}

Werin em berhev bikin. Dûv re bername dikare li ser du komputeran were xebitandin. An jî li ser yek, wek ku ez ê niha bikim. Em bi argumanên jêrîn TSark dest pê dikin:

$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x

Ger qada destpêkirinê ya di konsolê de tenê peyamek di derbarê destpêka girtinê de destnîşan dike, wê hingê ev nîşanek baş e - ev tê vê wateyê ku porta me bi îhtîmalek mezin ji hêla bernameyên din ve nayê dagîr kirin. Di termînalek din de, em mînakek bernameyekê didin destpêkirin ku dê bi destnîşankirina vê jimareya portê ve têkiliyek "dûr" simule bike:

$ ./mstest8 --port 9010 --lport 7010

Wekî ku ji nivîsara bernameyê tê dîtin, navnîşana IP-ya xwerû 127.0.0.1 e (loopback herêmî).

Di termînalek din de, em mînakek duyemîn a bernameyê dest pê dikin, ku amûrek herêmî simule dike. Em argumanek pêvek bikar tînin ku destûrê dide jeneratora testê ya çêkirî bixebite:

$ ./mstest8  --port 7010 --lport 9010 --gen 440

Di vê gavê de, pakêtên ku ber bi cîhaza "dûr" ve têne veguheztin divê di konsolê de bi TShark dest pê bikin, û dengek domdar dê ji axaftvana komputerê were bihîstin.

Ger her tişt wekî ku hatî nivîsandin çêbû, wê hingê em kopiya duyemîn a bernameyê ji nû ve dest pê dikin, lê bêyî mift û argumana "-gen 440". Niha hûn ê rola jeneratorê bilîzin. Piştî vê yekê, hûn dikarin di mîkrofonê de deng derxînin; divê hûn dengê têkildar di axaftvan an guhê de bibihîzin. Dibe ku xwe-heyîna akustîk jî çêbibe; dengê dengbêjê kêm bike û bandor dê winda bibe.

Ger we ew li ser du komputeran dimeşîne û di derheqê navnîşanên IP-yê de tevlihev nebû, wê hingê heman encam li benda we ye - pêwendiya dengê kalîteya dîjîtal a du-alî.

Di gotara paşîn de em ê fêr bibin ka meriv çawa fîlterên xwe - pêvekan binivîsîne, bi saya vê jêhatîbûnê hûn ê ne tenê ji bo deng û vîdyoyê, lê di heman demê de li hin deverên taybetî yên din jî bikar bînin weşana medyayê bikar bînin.

Source: www.habr.com

Add a comment