Uurime VoIP-mootorit Mediastreamer2. Osa 9

Artikli materjal on saadud minu Dzen-kanalist.

Dupleksne kõneside seade

Uurime VoIP-mootorit Mediastreamer2. Osa 9

Eelmisel artiklis Dupleksne kõneside seade on välja kuulutatud, ja selles artiklis käsitleme selle loomist.

Skeem on kujutatud pealkirjas. Alumine filtrite ahel moodustab edastuskanali, mis algab helikaardist. See väljastab signaali proovide eest mikrofoni. Vaikimisi toimub see kiirusel 8000 proovi sekundis. Andmete täpsus, mida helifiltrid mediariba jaoks kasutavad, on 16 bitti (see ei ole põhimõtteliselt oluline, soovi korral on võimalik kirjutada filtreid, mis töötavad suurema täpsusega). Andmed on grupeeritud blokkideks, iga blokk koosneb 160 proovist. Seega on iga bloki suurus 320 baiti. Seejärel edastame andmed generaatorisse, mis on välja lülitatud olekus andmete jaoks

Pärast generaatorit jõuab signaal kodeerijasse, mis teisendab meie 16-bitised proovid µ-seaduse (G.711 standard) alusel 8-bitisteks. Kodeerija väljundis on meil juba andmeblokk, mis on kaks korda väiksem. Üldiselt saame andmeid edastada tihendamata, kui me ei pea liiklust kokku hoidma. Kuid siin on kasulik kasutada kodeerijat, kuna Wireshark suudab RTP-voost heli esitada vaid siis, kui see on tihendatud µ-seaduse või a-seaduse alusel.

Pärast kodeerijat sisenevad kergendatud andmeblokid rtpsendi filtrisse, mis paigutab need RTP-paketti, seab vajalikud lipud ja edastab need mediastreamerile edastamiseks võrku UDP-pakendina.

Ülemine filtrite ahel loob vastuvõtukanali, RTP-paketid, mille mediastreamer on võrgust saanud, jõuavad rtprecv filtrisse, mille väljundis ilmuvad nad juba andmeblokkidena, millest igaüks vastab ühele vastuvõetud paketile. Blokk sisaldab ainult kasulikku koormust, eelmisel artiklis oli see kujutatud rohelise värviga.

Seejärel jõuavad blokid dekodeerija filtrisse, mis teisendab neis olevad ühe bite'i proovide lineaarseteks, 16-bittideks. Need saab juba töödelda mediastreameri filtritega. Meie juhul anname need lihtsalt helikaardile, et neid teie peakomplekti kõlarites esitada.

Nüüd liigume tarkvaralise teostuse juurde. Selleks ühendame vastuvõtja ja edastaja failid, mida me varem eraldasime. Varem kasutasime fikseeritud porte ja aadresse, kuid nüüd peab programm suutma kasutada neid seadistusi, mida me käivitamisel määrame. Selle jaoks lisame käsurea argumentide töötlemise funktsionaalsuse. Pärast seda saame määrata IP-aadressi ja porti, mille kaudu soovime side luua.

Esiteks lisame programmis struktuuri, mis säilitab selle seadeid:

struct _app_vars
{
  int  local_port;              /* Kohalik port. */
  int  remote_port;             /* Edastamise port kaugmasinas. */
  char remote_addr[128];        /* Kaugmasina IP-aadress. */
  MSDtmfGenCustomTone dtmf_cfg; /* Testsignaali generaatori seaded. */
};

typedef struct _app_vars app_vars;

Programmis kuulutatakse välja selle tüübi struktuur nimega vars.
Seejärel lisame käsurea argumentide analüüsi funktsiooni:

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

Analüüsi tulemusena paigutatakse käsurea argumendid vars struktuuri väljadele. Rakenduse põhifunktsioon kogub edastava ja vastuvõtva rada filtritest, pärast sidumise ühendamist antakse juhtimine igavesele tsüklile, mis, kui generaatori sagedus on määratud mitte nulliks, käivitab testgeneraatori uuesti - et see töötaks katkematult.

Need taaskäivitused on generaatori jaoks vajalikud tema ülesehituse tõttu, märkamatult ei saa ta anda signaali, mis kestab kauem kui 16 sekundit. Samas tuleb märkida, et kestus on määratud 32-bitise arvuga.

Programm tervikuna näeb välja selline:

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

Kompileerime. Edasi saab programmi käivitada kahes arvutis. Või ühes, nagu ma nüüd teen. Käivitame TSharki järgnevaid argumendiga:

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

Kui konsooli käivitamisväljal ilmub ainult sõnum seire algusest, siis on see hea märk - see tähendab, et meie port ei ole ilmselt teiste programmide poolt hõivatud. Teises terminalis käivitame programmi eksemplari, mis simuleerib "kaug" läbirääkimisseadet, määrates sellele selle porti numbri:

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

Nagu näete programmi tekstist, kasutatakse vaikimisi IP-aadressi 127.0.0.1 (kohalik silmus).

Teises terminalis käivitame teise programmi eksemplari, mis simuleerib kohalikku seadet. Kasutame täiendavat parameetrit, mis lubab sisseehitatud testigeneraatori tööd:

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

Sel hetkel peaks TSharki konsoolis hakkama vilkuma paketid, mis on suunatud "kaugseadmest", ja arvuti kõlaritest kostub pidev toon.

Kui kõik läks nagu kirjutatud, käivitame teise programmi eksemplari uuesti, kuid nüüd ilma võtme ja argumentideta "--gen 440". Generaatori rolli täidate nüüd teie. Pärast seda võite mikrofoni lähedal müra teha, kõlaritest või kõrvaklappidest peaksite kuulma vastavat heli. Võib isegi tekkida akustiline enesekeeramine, alandage kõlari helitugevust ja efekt kaob.

Kui olete käivitanud kahe arvuti peal ja ei ole IP-aadressides segadusse läinud, siis ootab teid sama tulemus — kahepoolne digitaalne kvaliteediga häälekõne.

Järgmises artiklis õpime kirjutama oma filtreid — pluginaid, tänu millele saate rakendada meediastrimmerit mitte ainult heli ja video jaoks, vaid ka mõnes muus spetsiifilises valdkonnas.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster