Esplora u mutore VoIP Mediastreamer2. Parte 9

U materiale di l'articulu hè pigliatu da u mo canale zen.

Intercom duplex

Esplora u mutore VoIP Mediastreamer2. Parte 9

In l'ultimu articulu un intercom duplex hè statu annunziatu, è in questu avemu da fà.

U diagramma hè mostratu in a figura di titulu. A catena più bassa di filtri forma a strada di trasmissione, chì parte da a carta di sonu. Fornisce campioni di signale da u microfonu. Per automaticamente, questu accade à una tarifa di 8000 campioni per seconda. A prufundità di bit di dati chì i filtri audio media streamer usanu hè 16 bits (questu ùn hè micca impurtante; se vulete, pudete scrive filtri chì funzionanu cù una prufundità di bit più altu). I dati sò raggruppati in blocchi di 160 campioni. Cusì, ogni bloccu hè 320 bytes in grandezza. In seguitu, avemu alimentatu i dati à l'input di u generatore, chì, quandu si spegne, hè "trasparente" à i dati. L'aghju aghjustatu in casu chì vi stancu di parlà in u micru durante a debugging - pudete aduprà u generatore per "sparà" a strada cù un signalu di tonu.

Dopu à u generatore, u signale passa à l'encoder, chì cunverte i nostri campioni di 16 bits secondu a lege µ (standard G.711) in ottu bit. À a pruduzzioni di l'encoder, avemu digià un bloccu di dati a mità di a dimensione. In generale, pudemu trasmette dati senza cumpressione si ùn avemu micca bisognu di salvà u trafficu. Ma quì hè utile aduprà un codificatore, postu chì Wireshark pò ripruduce l'audio da un flussu RTP solu quandu hè cumpressu secondu a lege µ o a lege.

Dopu à l'encoder, i blocchi più ligeri di dati sò mandati à u filtru rtpsend, chì i mette in un pacchettu RTP, stabilisce i bandieri necessarii è dà à u media streamer per a trasmissione nantu à a reta in forma di un pacchettu UDP.

A catena superiore di i filtri forma u percorsu di ricezione; i pacchetti RTP ricevuti da u media streamer da a reta entranu in u filtru rtprecv, à l'output di quale appariscenu in forma di blocchi di dati, ognunu di i quali currisponde à un pacchettu ricevutu. U bloccu cuntene solu dati di carica; in l'articulu precedente sò stati mostrati in verde in l'illustrazione.

In seguitu, i blocchi sò mandati à u filtru di decodificatore, chì cunverta i campioni di un byte cuntenuti in elli in lineari, 16-bit. Chì pò digià esse trattatu da filtri media streamer. In u nostru casu, simpricimenti li mandemu à a carta di sonu per a riproduzione nantu à i parlanti di i vostri cuffie.

Avà andemu à l'implementazione di u software. Per fà questu, combineremu i schedarii di ricevitore è trasmettitore chì avemu separatu prima. Prima di questu, avemu usatu paràmetri fissi per i porti è l'indirizzi, ma avà avemu bisognu di u prugramma per pudè utilizà i paràmetri chì avemu specificatu à l'iniziu. Per fà questu, aghjustemu a funziunalità per processà l'argumenti di a linea di cummanda. Dopu à quale pudemu stabilisce l'indirizzu IP è u portu di l'intercom cù quale vulemu stabilisce una cunnessione.

Prima, aghjustemu una struttura à u prugramma chì guardà i so paràmetri:

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

typedef struct _app_vars app_vars;

U prugramma dichjarà una struttura di stu tipu chjamata vars.
In seguitu, aghjustemu una funzione per analizà l'argumenti di a linea di cummanda:

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

In u risultatu di l'analisi, l'argumenti di a linea di cumanda seranu posti in i campi di a struttura vars. A funzione principale di l'applicazione serà di cullà i percorsi di trasmissione è di ricezione da i filtri; dopu avè cunnessu u ticker, u cuntrollu serà trasferitu à un ciclu infinitu chì, se a frequenza di u generatore hè stata impostata à micca zero, riavviarà u generatore di prova in modu chì funziona senza piantà.

U generatore avarà bisognu di sti riavvii per via di u so disignu; per una certa ragione ùn pò micca pruduce un signalu chì dura più di 16 seconde. Si deve esse nutatu chì a so durata hè specificatu da un numeru 32-bit.

Tuttu u prugramma sarà cusì:

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

Cumpilemu. Allora u prugramma pò esse eseguitu nantu à dui computer. O nantu à unu, cum'è aghju da fà avà. Lancemu TShark cù i seguenti argumenti:

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

Se u campu di lanciamentu in a cunsola mostra solu un missaghju annantu à l'iniziu di a cattura, allora questu hè un bonu signu - significa chì u nostru portu ùn hè micca prubabilmente occupatu da altri prugrammi. In un altru terminal, lancemu una istanza di prugramma chì simulerà un intercom "remotu" specificendu stu numeru di portu:

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

Comu pò esse vistu da u testu di u prugramma, l'indirizzu IP predeterminatu hè 127.0.0.1 (loopback locale).

In un altru terminal, lanciamu una seconda istanza di u prugramma, chì simula un dispositivu lucale. Utilizemu un argumentu supplementu chì permette à u generatore di teste integratu per travaglià:

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

À questu mumentu, i pacchetti trasmessi versu u dispositivu "remotu" deve cumincià à lampassi in a cunsola cù TShark, è un tonu cuntinuu serà intesu da u parlante di l'urdinatore.

Se tuttu hè accadutu cum'è scrittu, allora ripigliamu a seconda copia di u prugramma, ma senza a chjave è l'argumentu "-gen 440". Avete avà ghjucà u rolu di generatore. Dopu questu, pudete fà u sonu in u micrufonu; duvete sente u sonu currispondente in u parlante o l'auriculare. L'auto-eccitazione acustica pò ancu esse; diminuite u voluminu di u parlante è l'effettu sparirà.

Se l'avete currettu in dui computers è ùn avete micca cunfunditu nantu à l'indirizzi IP, allora u listessu risultatu vi aspetta - cumunicazione vocale di qualità digitale bidirezionale.

In u prossimu articulu avemu da amparà à scrive i nostri filtri - plugins, grazia à sta cumpetenza, puderà utilizà u media streamer micca solu per l'audio è u video, ma ancu in qualchì altra zona specifica.

Source: www.habr.com

Add a comment