Njelajah mesin Mediastreamer2 VoIP. Bagean 9

Materi artikel dijupuk saka sandi saluran zen.

Interkom duplex

Njelajah mesin Mediastreamer2 VoIP. Bagean 9

Ing pungkasan artikel interkom duplex diumumake, lan ing siji iki kita bakal nggawe.

Diagram ditampilake ing gambar judhul. Rantai saringan ngisor mbentuk jalur transmisi, sing diwiwiti saka kertu swara. Iki nyedhiyakake conto sinyal saka mikropon. Kanthi gawan, iki dumadi ing tingkat 8000 conto per detik. Ambane bit data sing digunakake saringan audio streamer media yaiku 16 bit (iki ora penting; yen sampeyan pengin, sampeyan bisa nulis saringan sing bisa digunakake kanthi ambane bit sing luwih dhuwur). Data kasebut dikelompokake dadi blok 160 sampel. Dadi, saben blok ukurane 320 bita. Sabanjure, kita feed data menyang input generator, sing, nalika dipateni, "transparan" kanggo data. Aku nambahake yen sampeyan kesel ngomong menyang mikropon nalika debugging - sampeyan bisa nggunakake generator kanggo "nembak" dalan kanthi sinyal nada.

Sawise generator, sinyal kasebut menyang encoder, sing ngowahi conto 16-bit kita miturut hukum μ (standar G.711) dadi wolung bit. Ing output encoder, kita wis duwe blok data setengah ukuran. Umumé, kita bisa ngirim data tanpa kompresi yen kita ora perlu ngirit lalu lintas. Nanging ing kene migunani kanggo nggunakake encoder, amarga Wireshark bisa ngasilake audio saka stream RTP mung nalika dikompres miturut µ-law utawa a-law.

Sawise encoder, pamblokiran data sing luwih entheng dikirim menyang filter rtpsend, sing bakal dilebokake ing paket RTP, nyetel panji sing dibutuhake lan menehi menyang streamer media kanggo transmisi liwat jaringan ing wangun paket UDP.

Rantai saringan ndhuwur mbentuk jalur panampa; Paket RTP sing ditampa dening streamer media saka jaringan ketik filter rtprecv, ing output sing katon ing wangun blok data, sing saben cocog karo siji paket sing ditampa. Blok kasebut mung ngemot data muatan; ing artikel sadurunge ditampilake kanthi warna ijo ing ilustrasi.

Sabanjure, pamblokiran dikirim menyang panyaring dekoder, sing ngowahi conto bait siji sing ana ing kana dadi linear, 16-bit. Sing wis bisa diproses dening saringan streamer media. Ing kasus kita, kita mung ngirim menyang kertu swara kanggo muter maneh ing speaker headset.

Saiki ayo pindhah menyang implementasine piranti lunak. Kanggo nindakake iki, kita bakal nggabungake file panrima lan pemancar sing dipisahake sadurunge. Sadurunge iki, kita nggunakake setelan tetep kanggo port lan alamat, nanging saiki kita butuh program supaya bisa nggunakake setelan sing kita nemtokake nalika wiwitan. Kanggo nindakake iki, kita bakal nambah fungsi kanggo ngolah argumen baris perintah. Sawisé iku, kita bakal bisa nyetel alamat IP lan port saka interkom karo kang kita arep kanggo nggawe sambungan.

Pisanan, ayo nambah struktur menyang program sing bakal nyimpen setelan:

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

typedef struct _app_vars app_vars;

Program kasebut bakal ngumumake struktur jinis iki sing diarani vars.
Sabanjure, ayo nambah fungsi kanggo ngurai argumen baris perintah:

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

Minangka asil parsing, argumen baris perintah bakal diselehake ing kolom struktur vars. Fungsi utama aplikasi kasebut yaiku kanggo ngumpulake jalur ngirim lan nampa saka saringan; sawise nyambungake ticker, kontrol bakal ditransfer menyang loop tanpa wates sing, yen frekuensi generator disetel dadi non-nol, bakal miwiti maneh generator test supaya kerjane tanpa mandheg.

Generator mbutuhake urip maneh amarga desaine, amarga ora bisa ngasilake sinyal sing tahan luwih saka 16 detik. Perlu dicathet menawa durasi kasebut ditemtokake dening nomer 32-bit.

Kabeh program bakal katon kaya iki:

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

Ayo kompilasi. Banjur program bisa mbukak ing rong komputer. Utawa siji, kaya sing bakal daklakoni saiki. Kita miwiti TShark kanthi argumen ing ngisor iki:

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

Yen lapangan peluncuran ing konsol mung nampilake pesen babagan wiwitan panangkepan, mula iki minangka tandha apik - tegese port kita paling ora dikuwasani dening program liyane. Ing terminal liyane, kita miwiti conto program sing bakal simulasi interkom "remote" kanthi nemtokake nomer port iki:

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

Minangka bisa dideleng saka teks program, alamat IP standar yaiku 127.0.0.1 (localback lokal).

Ing terminal liyane, kita miwiti conto kapindho program, kang simulates piranti lokal. Kita nggunakake argumen tambahan sing ngidini generator tes sing dibangun ing:

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

Ing wayahe, paket sing dikirim menyang piranti "remote" kudu miwiti kerlip ing console karo TShark, lan muni terus-terusan bakal krungu saka speaker komputer.

Yen kabeh kedadeyan kaya sing ditulis, mula maneh salinan program kapindho, nanging tanpa tombol lan argumen "-gen 440". Sampeyan saiki bakal muter peran generator. Sawise iki, sampeyan bisa nggawe gangguan menyang mikropon; sampeyan kudu krungu swara sing cocog ing speaker utawa headphone. Eksitasi diri akustik bisa uga kedadeyan; mateni volume speaker lan efek bakal ilang.

Yen sampeyan mbukak ing rong komputer lan ora bingung babagan alamat IP, sampeyan bakal entuk asil sing padha - komunikasi swara kualitas digital rong arah.

Ing artikel sabanjure, kita bakal sinau carane nulis saringan kita dhewe - plugin, amarga katrampilan iki sampeyan bakal bisa nggunakake streamer media ora mung kanggo audio lan video, nanging uga ing sawetara wilayah tartamtu liyane.

Source: www.habr.com

Add a comment