Studimi i motorit VoIP Mediastreamer2. Pjesa 9

Materiali i artikullit është marrë nga kanali im zen.

Dispositiv dyal bisedhë

Studimi i motorit VoIP Mediastreamer2. Pjesa 9

Në të kaluarën artikullin u shpall dispozitivi dyal bisedhë, dhe në këtë ne do ta krijojmë.

Skema është e paraqitur në figurën kryesore. Zgjidhja e filtrave të poshtme përbën trajektoren transmetuese, që fillon nga karta audio. Ajo lëshon mostra sinjali nga mikrofonit. Në parazgjedhje, kjo ndodh me një ritëm prej 8000 mostrash në sekondë. Cilësia e të dhënave që përdorin filtrat audio të mediastreamer-it është 16 bit (kjo nuk është e detyrueshme, nëse dëshirohet mund të shkruhen filtra që do të funksionojnë me cilësi më të lartë). Të dhënat grupohen në blloqe prej 160 mostrash. Kështu, çdo bllok ka madhësi 320 byte. Më pas ne i japim të dhënat në hyrje të gjeneratorit, i cili në gjendje të fikur është "transparent" për të dhënat. E kam shtuar atë në rast se gjatë rregullimit ju mërzit të flisni në mikrofon - do të mund të përdorni gjeneratorin për të "shkrehur" trajektoren me një sinjal tonal.

Pas pas gjeneratorit, sinjali kalon te koduesi, i cili shndërron regjistrimet tona 16-bitësh sipas µ-ligjit (standardi G.711) në tetë bitë. Në daljen e koduesit, ne tashmë kemi një bllok të dhënash me madhësi dyfish më të vogël. Në përgjithësi, ne mund të transmetojmë të dhëna pa compression, nëse nuk na nevojitet të kursejmë trafik. Por këtu është e dobishme të përdorim koduesin, pasi Wireshark mund të riprodhojë tinguj nga rrjedha RTP vetëm kur ajo është e kompresuar sipas µ-ligjit ose a-ligjit.

Pas koduesit, blloqet e dhënash të lehtësuara kalojnë në filtrin rtpsend, i cili do t'i vendosë ato në paketën RTP, do të vendosë flagat e nevojshme dhe do t'i dërgojë atyre mediastreamerit për t'u transmetuar në rrjet si një paketë UDP.

Shkalla e filtrave të sipërme formon traktin e pranimit; paketat RTP, të marra nga mediastreameri nga rrjeti, kalojnë në filtrin rtprecv, nga dalja e të cilit, ato shfaqen tashmë si blloqe të dhënash, çdo një prej të cilave korrespondon me një paketë të pranuar. Blloku përmban vetëm të dhënat e ngarkesës, në artikullin e kaluar ato u treguan në ngjyrë të gjelbër.

Më pas, blloket kalojnë në filtrin e dekoderit, i cili shndërron të dhënat njëbyte në linera 16-bit, të cilat mund të përpunohen nga filtrat e mediastreamer-it. Në rastin tonë, ne i japim thjesht në kartën audio për të luajtur në altoparlantët e pajisjes tuaj.

Tani le të kalojmë në realizimin programor. Për këtë, ne do të bashkojmë skedarët e marrësit dhe dërguesit, të cilat i kemi ndarë më parë. Më parë, ne përdornim konfigurime fiks të porteve dhe adresave, por tani na duhen që programi të mund të përdorë ato konfigurime që ne do të specifikojmë gjatë nisjes. Për këtë, ne do të shtojmë funksionalitetin e trajtimit të argumenteve të komandës. Pas kësaj, do të jemi në gjendje të caktuam adresën IP dhe portin e pajisjes të bisedës, me të cilën dëshirojmë të vendosim lidhjen.

Së pari, do të shtojmë në program një strukturë që do të ruajë konfigurimet e saj:

struct _app_vars
{
  int  local_port;              /* Porta lokale. */
  int  remote_port;             /* Porta e pajisjes negociuese në kompjuterin e largët. */
  char remote_addr[128];        /* Adresa IP e kompjuterit të largët. */
  MSDtmfGenCustomTone dtmf_cfg; /* Cilësimet e sinjalit të testit të gjeneratorit. */
};

typedef struct _app_vars app_vars;

Në program do të shpallet një strukturë e këtij tipi me emrin vars.
Më pas do të shtojmë funksionin e analizës së argumenteve të komandës:

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

Si rezultat i analizës, argumentet e komandës do të vendosen në fushat e strukturës vars. Funksioni kryesor i aplikacionit do të ndërtojë nga filtrat rrugët e transmetimit dhe pranimit, pasi lidhja e ticker-it do ta kalojë kontrollin në një cikël të pafund që, nëse frekuenca e gjeneratorit është caktuar e ndryshme nga zero, do të rilidhet gjeneratori i testit - në mënyrë që të funksionojë pa ndërprerje.

Këto rilidhje do të jenë të nevojshme për gjeneratorin për shkak të veçorive të tij të ndërtimit, për një arsye a tjetër ai nuk mund të japë një sinjal që zgjat më shumë se 16 sekonda. Në të njëjtën kohë, duhet theksuar se gjatëzgjatja është e caktuar me një numër 32-bit.

Programi në tërësi do të duket kështu:

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

Ne pohojè. Më pas, programi mund të startohet në dy kompjuterë. Ose në një, siç do të bëj tani. Po fillojmë TShark me këto argumente:

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

Nëse fusha e ekzekutimit në konsolë shfaq vetëm mesazhin e fillimit të kapjes, atëherë kjo është një shenjë e mirë - do të thotë që porta jonë për fat nuk është e zënë nga programe të tjera. Në një terminal tjetër, po fillojmë ekzemplarën e programit që do të imitojë "dispositivin" e bisedës duke i dhënë këtij numri porti:

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

Siç tregohet nga teksti i programit, adresa IP e vendosur për default është 127.0.0.1 (loopback lokale).

Edhe në një terminal tjetër, po fillojmë ekzemplarën e dytë të programit, e cila imiton një dispozitiv lokal. Përdorim një argument shtesë që lejon funksionimin e gjeneratorit të testit të integruar:

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

Në këtë moment, në konsolën me TShark duhet të fillojnë të shfaqen paketat që kalojnë drejt "dispositivit" të largët, ndërsa nga altoparlanti i kompjuterit do të dëgjohet një sinjal tons përherë.

Nëse gjithçka ndodhi siç ishte planifikuar, tani ne rihapim ekzemplarin e dytë të programit, por tani pa çelësin dhe argumentin "—gen 440". Roli i gjeneratorit tani do të jetë i juaji. Pas kësaj, mund të bëni zë në mikrofon, në altoparlant ose kufje, ju duhet të dëgjoni tingullin përkatës. Mund të shfaqet madje edhe automotivimi akustik, ulni volumet e altoparlantit dhe efekti do të zhduket.

Nëse keni hapur në dy kompjuterë dhe nuk jeni ngatërruar me adresat IP, ju pret të njëjtin rezultat — komunikim dyansh më cilësi digjitale.

Në artikullin e ardhshëm, do të mësojmë si të shkruajmë filtrat tanë — plugins, falë kësaj aftësie do të jeni në gjendje të përdorni media-streamer jo vetëm për zë dhe video, por edhe në ndonjë fushë tjetër specifike.

Burimi: habr.com

Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë 🔥 Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë | ProHoster