Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 9

Ang materyal ng artikulo ay kinuha mula sa aking zen channel.

Duplex intercom

Paggalugad sa Mediastreamer2 VoIP engine. Bahagi 9

Sa huli Artikulo isang duplex intercom ang inihayag, at sa isang ito ay gagawin natin ito.

Ang diagram ay ipinapakita sa figure ng pamagat. Ang mas mababang kadena ng mga filter ay bumubuo sa landas ng paghahatid, na nagsisimula sa sound card. Nagbibigay ito ng mga sample ng signal mula sa mikropono. Bilang default, nangyayari ito sa bilis na 8000 sample bawat segundo. Ang bit depth ng data na ginagamit ng mga media streamer audio filter ay 16 bits (hindi ito mahalaga; kung gusto mo, maaari kang magsulat ng mga filter na gagana nang may mas mataas na bit depth). Ang data ay pinagsama-sama sa mga bloke ng 160 sample. Kaya, ang bawat bloke ay 320 bytes ang laki. Susunod, pinapakain namin ang data sa input ng generator, na, kapag naka-off, ay "transparent" sa data. Idinagdag ko ito kung sakaling mapagod ka sa pakikipag-usap sa mikropono habang nagde-debug - maaari mong gamitin ang generator upang "i-shoot" ang landas na may signal ng tono.

Pagkatapos ng generator, ang signal ay mapupunta sa encoder, na nagko-convert sa aming 16-bit na mga sample ayon sa Β΅-law (G.711 standard) sa walong-bit na mga sample. Sa output ng encoder, mayroon na kaming data block na kalahati ng laki. Sa pangkalahatan, maaari kaming magpadala ng data nang walang compression kung hindi namin kailangang i-save ang trapiko. Ngunit narito, kapaki-pakinabang na gumamit ng isang encoder, dahil ang Wireshark ay maaaring magparami ng audio mula sa isang RTP stream lamang kapag ito ay na-compress ayon sa Β΅-law o a-law.

Pagkatapos ng encoder, ang mas magaan na mga bloke ng data ay ipinapadala sa rtpsend filter, na maglalagay sa kanila sa isang RTP packet, itakda ang mga kinakailangang flag at ibigay ang mga ito sa media streamer para sa paghahatid sa network sa anyo ng isang UDP packet.

Ang itaas na kadena ng mga filter ay bumubuo sa landas ng pagtanggap; Ang mga RTP packet na natanggap ng media streamer mula sa network ay pumasok sa rtprecv filter, sa output kung saan lumilitaw ang mga ito sa anyo ng mga bloke ng data, na ang bawat isa ay tumutugma sa isang natanggap na packet. Ang block ay naglalaman lamang ng data ng payload; sa nakaraang artikulo ay ipinakita ang mga ito sa berde sa ilustrasyon.

Susunod, ang mga bloke ay ipinadala sa decoder filter, na nagko-convert sa mga single-byte na sample na nakapaloob sa mga ito sa mga linear, 16-bit na mga sample. Na maaaring maproseso na ng mga filter ng media streamer. Sa aming kaso, ipinapadala lang namin sila sa sound card para sa pag-playback sa mga speaker ng iyong headset.

Ngayon ay lumipat tayo sa pagpapatupad ng software. Para magawa ito, pagsasamahin namin ang mga file ng receiver at transmitter na pinaghiwalay namin noon. Bago ito, gumamit kami ng mga nakapirming setting para sa mga port at address, ngunit ngayon kailangan namin ng program upang magamit ang mga setting na aming tinukoy sa pagsisimula. Para magawa ito, magdaragdag kami ng functionality para sa pagproseso ng mga argumento ng command line. Pagkatapos nito, magagawa naming itakda ang IP address at port ng intercom kung saan nais naming magtatag ng isang koneksyon.

Una, magdagdag tayo ng istraktura sa program na mag-iimbak ng mga setting nito:

struct _app_vars
{
  int  local_port;              /* Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΠΎΡ€Ρ‚. */
  int  remote_port;             /* ΠŸΠΎΡ€Ρ‚ ΠΏΠ΅Ρ€Π΅Π³ΠΎΠ²ΠΎΡ€Π½ΠΎΠ³ΠΎ устройства Π½Π° ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠΌ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π΅. */
  char remote_addr[128];        /* IP-адрСс ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°. */
  MSDtmfGenCustomTone dtmf_cfg; /* Настройки тСстового сигнала Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€Π°. */
};

typedef struct _app_vars app_vars;

Ang programa ay magdedeklara ng isang istraktura ng ganitong uri na tinatawag na vars.
Susunod, magdagdag tayo ng function para i-parse ang mga argumento ng command line:

/* Ѐункция прСобразования Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки Π²
* настройки ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹. */
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]);
        }
    }
}

Bilang resulta ng pag-parse, ang mga argumento ng command line ay ilalagay sa mga field ng vars structure. Ang pangunahing pag-andar ng application ay upang mangolekta ng pagpapadala at pagtanggap ng mga landas mula sa mga filter; pagkatapos ikonekta ang ticker, ang kontrol ay ililipat sa isang walang katapusang loop na kung saan, kung ang dalas ng generator ay nakatakda sa non-zero, ay muling simulan ang pagsubok generator upang gumagana ito nang walang tigil.

Kakailanganin ng generator ang mga pag-restart na ito dahil sa disenyo nito; sa ilang kadahilanan ay hindi ito makagawa ng signal na tumatagal ng higit sa 16 na segundo. Dapat tandaan na ang tagal nito ay tinukoy ng isang 32-bit na numero.

Ang buong programa ay magiging ganito:

/* Π€Π°ΠΉΠ» 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);
    }
}

Mag-compile tayo. Pagkatapos ang programa ay maaaring tumakbo sa dalawang computer. O sa isa, gaya ng gagawin ko ngayon. Inilunsad namin ang TShark na may mga sumusunod na argumento:

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

Kung ang patlang ng paglunsad sa console ay nagpapakita lamang ng isang mensahe tungkol sa pagsisimula ng pagkuha, kung gayon ito ay isang magandang senyales - nangangahulugan ito na ang aming port ay malamang na hindi inookupahan ng iba pang mga programa. Sa isa pang terminal, naglulunsad kami ng isang instance ng program na gagayahin ang isang "remote" na intercom sa pamamagitan ng pagtukoy sa port number na ito:

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

Tulad ng makikita mula sa teksto ng programa, ang default na IP address ay 127.0.0.1 (lokal na loopback).

Sa isa pang terminal, naglulunsad kami ng pangalawang pagkakataon ng programa, na ginagaya ang isang lokal na device. Gumagamit kami ng karagdagang argumento na nagpapahintulot sa built-in na test generator na gumana:

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

Sa sandaling ito, ang mga packet na ipinadala patungo sa "remote" na aparato ay dapat magsimulang mag-flash sa console gamit ang TShark, at isang tuluy-tuloy na tono ang maririnig mula sa speaker ng computer.

Kung nangyari ang lahat tulad ng nakasulat, pagkatapos ay i-restart namin ang pangalawang kopya ng programa, ngunit walang susi at argumento "β€”gen 440". Gagampanan mo na ngayon ang papel ng generator. Pagkatapos nito, maaari kang gumawa ng ingay sa mikropono; dapat mong marinig ang kaukulang tunog sa speaker o mga headphone. Maaaring mangyari ang acoustic self-excitation; hinaan ang volume ng speaker at mawawala ang epekto.

Kung pinatakbo mo ito sa dalawang computer at hindi nalilito tungkol sa mga IP address, ang parehong resulta ay naghihintay sa iyo - two-way na digital na kalidad na komunikasyon ng boses.

Sa susunod na artikulo matututunan natin kung paano magsulat ng sarili nating mga filter - mga plugin, salamat sa kasanayang ito, magagamit mo ang media streamer hindi lamang para sa audio at video, kundi pati na rin sa ilang iba pang partikular na lugar.

Pinagmulan: www.habr.com

Magdagdag ng komento