Ferkenne de Mediastreamer2 VoIP-motor. Diel 9

It materiaal fan it artikel is nommen út myn zen kanaal.

Duplex yntercom

Ferkenne de Mediastreamer2 VoIP-motor. Diel 9

Yn de lêste artikel in duplex intercom waard oankundige, en yn dizze sille wy meitsje it.

It diagram wurdt werjûn yn 'e titelfiguer. De legere keten fan filters foarmet it oerdrachtpaad, dat begjint fan 'e lûdkaart. It leveret sinjaalmonsters fan 'e mikrofoan. Standert komt dit foar mei in taryf fan 8000 samples per sekonde. De databitdjipte dy't mediastreamer audiofilters brûke is 16 bits (dit is net wichtich; as jo wolle, kinne jo filters skriuwe dy't wurkje mei in hegere bitdjipte). De gegevens binne groepearre yn blokken fan 160 samples. Sa is elk blok 320 bytes yn grutte. Dêrnei fiede wy de gegevens oan 'e ynfier fan' e generator, dy't, as it útskeakele is, "transparant" is foar de gegevens. Ik haw it tafoege foar it gefal dat jo wurch wurde fan praten yn 'e mikrofoan tidens debuggen - jo kinne de generator brûke om it paad te "sjitten" mei in toansignal.

Nei de generator giet it sinjaal nei de encoder, dy't ús 16-bit samples konvertearret neffens de µ-wet (G.711 standert) yn acht-bit ones. Oan 'e útfier fan' e encoder hawwe wy al in gegevensblok de helte fan 'e grutte. Yn 't algemien kinne wy ​​gegevens sûnder kompresje ferstjoere as wy gjin ferkear hoege te bewarjen. Mar hjir is it handich om in encoder te brûken, om't Wireshark audio kin reprodusearje fan in RTP-stream allinich as it komprimearre is neffens de µ-wet of a-wet.

Nei de encoder wurde de lichtere blokken fan gegevens stjoerd nei it rtpsend-filter, dat se yn in RTP-pakket sil pleatse, de nedige flaggen ynstelle en jouwe se oan 'e mediastreamer foar oerdracht oer it netwurk yn' e foarm fan in UDP-pakket.

De boppeste keten fan filters foarmet it ûntfangende paad; RTP-pakketten ûntfongen troch de mediastreamer fan it netwurk geane yn it rtprecv-filter yn, by de útfier wêrfan se ferskine yn 'e foarm fan gegevensblokken, dy't elk oerienkomme mei ien ûntfongen pakket. It blok befettet allinich nuttige ladinggegevens; yn it foarige artikel waarden se yn grien werjûn yn 'e yllustraasje.

Folgjende wurde de blokken stjoerd nei it dekoderfilter, dat de single-byte-samples yn har konvertearret yn lineêre, 16-bit. Wat al kin wurde ferwurke troch mediastreamerfilters. Yn ús gefal stjoere wy se gewoan nei de lûdskaart foar ôfspieljen op 'e sprekkers fan jo headset.

Litte wy no trochgean nei software-ymplemintaasje. Om dit te dwaan, sille wy de ûntfanger- en stjoerderbestannen kombinearje dy't wy earder skieden. Dêrfoar brûkten wy fêste ynstellings foar havens en adressen, mar no hawwe wy it programma nedich om de ynstellingen te brûken dy't wy opjaan by it opstarten. Om dit te dwaan, soene wy ​​funksjonaliteit tafoegje foar it ferwurkjen fan kommandorigelarguminten. Dêrnei sille wy it IP-adres en de poarte fan 'e yntercom kinne ynstelle wêrmei't wy in ferbining meitsje wolle.

Litte wy earst in struktuer tafoegje oan it programma dy't syn ynstellingen sil opslaan:

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

typedef struct _app_vars app_vars;

It programma sil ferklearje in struktuer fan dit type neamd vars.
Litte wy dan in funksje tafoegje om arguminten foar kommandorigel te parsearjen:

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

As gefolch fan parsing wurde de kommandorigelarguminten pleatst yn 'e fjilden fan' e vars-struktuer. De haadfunksje fan 'e applikaasje sil wêze it sammeljen fan ferstjoer- en ûntfangende paden fan filters; nei it ferbinen fan de ticker sil kontrôle wurde oerbrocht nei in ûneinige lus dy't, as de generatorfrekwinsje op non-nul is ynsteld, de testgenerator opnij sil starte, sadat it wurket sûnder ophâlden.

De generator sil dizze opstarten nedich wêze fanwegen syn ûntwerp; om ien of oare reden kin it gjin sinjaal produsearje dy't mear dan 16 sekonden duorret. Dêrby moat opmurken wurde dat syn doer wurdt oantsjutte troch in 32-bit nûmer.

It hiele programma sil der sa útsjen:

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

Litte wy kompilearje. Dan kin it programma op twa kompjûters rinne. Of op ien, lykas ik no dwaan sil. Wy lansearje TShark mei de folgjende arguminten:

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

As it startfjild yn 'e konsole allinich in berjocht toant oer it begjin fan it fangen, dan is dit in goed teken - it betsjut dat ús poarte nei alle gedachten net beset is troch oare programma's. Yn in oare terminal lansearje wy in programma-eksimplaar dy't in "ôfstân" yntercom sil simulearje troch dit poartenûmer op te jaan:

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

As kin sjoen wurde út de programma tekst, it standert IP-adres is 127.0.0.1 (lokale loopback).

Yn in oare terminal lansearje wy in twadde eksimplaar fan it programma, dat in lokaal apparaat simulearret. Wy brûke in ekstra argumint wêrtroch de ynboude testgenerator wurket:

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

Op dit stuit moatte pakketten oerstjoerd nei it "ôfstân" apparaat moatte begjinne te flitsen yn 'e konsole mei TShark, en in trochgeande toan sil wurde heard fan' e kompjûtersprekker.

As alles barde lykas skreaun, dan werstart wy de twadde kopy fan it programma, mar sûnder de kaai en argumint "—gen 440". Jo sille no spylje de rol fan generator. Hjirnei kinne jo lûd meitsje yn 'e mikrofoan; jo moatte it korrespondearjende lûd hearre yn 'e sprekker of koptelefoan. Akoestyske sels-excitation kin sels foarkomme; draai it sprekkervolumint del en it effekt sil ferdwine.

As jo ​​​​it op twa kompjûters rûnen en net yn 'e war wiene oer de IP-adressen, dan wachtet jo itselde resultaat - twa-wei digitale kwaliteit stimkommunikaasje.

Yn it folgjende artikel sille wy leare hoe't jo ús eigen filters skriuwe - plugins, troch dizze feardigens kinne jo de mediastreamer brûke net allinich foar audio en fideo, mar ek yn in oar spesifyk gebiet.

Boarne: www.habr.com

Add a comment