Mediastreamer2 VoIP motorra arakatzen. 9. zatia

Artikuluaren materiala niretik hartua da zen kanala.

duplex interfonoa

Mediastreamer2 VoIP motorra arakatzen. 9. zatia

Azkenean Artikulu duplex interfono bat iragarri zen, eta honetan egingo dugu.

Diagrama izenburuko irudian ageri da. Beheko iragazki-kateak transmisio-bidea osatzen du, soinu-txarteletik abiatzen dena. Mikrofonotik seinale-laginak eskaintzen ditu. Lehenespenez, segundoko 8000 laginko abiaduran gertatzen da. Media streamer audio-iragazkiak erabiltzen duten datu-bit-sakonera 16 bitekoa da (hau ez da garrantzitsua; nahi izanez gero, bit-sakonera handiagoarekin funtzionatuko duten iragazkiak idatz ditzakezu). Datuak 160 laginetako blokeetan biltzen dira. Horrela, bloke bakoitzak 320 byte ditu. Ondoren, datuak sorgailuaren sarrerara elikatzen ditugu, eta hori, itzalita dagoenean, datuekiko "gardena" da. Araztean mikrofonoarekin hitz egiteaz nekatzen bazara gehitu dut: sorgailua erabil dezakezu tonu seinale batekin bidea "tiro egiteko".

Sorgailuaren ondoren, seinalea kodegailura doa eta honek Β΅-legearen arabera (G.16 estandarra) gure 711 biteko laginak zortzi bitekotan bihurtzen ditu. Kodetzailearen irteeran, dagoeneko tamaina erdiaren datu-bloke bat dugu. Oro har, datuak konpresiorik gabe transmiti ditzakegu trafikoa aurreztu behar ez badugu. Baina hemen erabilgarria da kodetzailea erabiltzea, izan ere, Wireshark-ek RTP korronte batetik audioa erreproduzi dezake Β΅-legearen edo a-legearen arabera konprimitzen denean soilik.

Kodetzailearen ondoren, datu-bloke arinagoak rtpsend iragazkira bidaltzen dira, RTP pakete batean sartuko ditu, beharrezko banderak ezarri eta multimedia streamer-ari emango dizkio sarean bidaltzeko UDP pakete moduan.

Iragazkien goiko kateak eratzen du jasotzeko bidea; saretik multimedia-streamer-ek jasotako RTP paketeak rtprecv iragazkia sartzen dira, eta irteeran datu-bloke moduan agertzen dira, bakoitza jasotako pakete bati dagokio. Blokeak karga-datuak soilik ditu; aurreko artikuluan ilustrazioan berdez agertzen ziren.

Ondoren, blokeak deskodetzaile-iragazkira bidaltzen dira, eta haietan dauden byte bakarreko laginak 16 biteko linealetan bihurtzen ditu. Dagoeneko multimedia-streamer-iragazkiek prozesatu dezaketena. Gure kasuan, soinu-txartelera bidaltzen ditugu entzungailuen bozgorailuetan erreproduzitzeko.

Orain pasa gaitezen softwarearen ezarpenera. Horretarako, aurretik bereizi ditugun hartzaile eta igorle fitxategiak konbinatuko ditugu. Aurretik, portuetarako eta helbideetarako ezarpen finkoak erabiltzen genituen, baina orain programa behar dugu abiaraztean zehazten ditugun ezarpenak erabili ahal izateko. Horretarako, komando lerroko argumentuak prozesatzeko funtzionalitateak gehituko genituzke. Horren ostean, konexio bat ezarri nahi dugun interfonoaren IP helbidea eta ataka ezarri ahal izango ditugu.

Lehenik eta behin, gehi diezaiogun bere ezarpenak gordeko dituen programari egitura bat:

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

typedef struct _app_vars app_vars;

Programak vars izeneko mota honetako egitura bat aldarrikatuko du.
Ondoren, gehi dezagun funtzio bat komando-lerroko argumentuak analizatzeko:

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

Analisiaren ondorioz, komando-lerroko argumentuak vars egiturako eremuetan jarriko dira. Aplikazioaren funtzio nagusia iragazkietatik transmititzeko eta jasotzeko bideak biltzea izango da; ticker-a konektatu ondoren, kontrola begizta infinitu batera transferituko da eta, sorgailuaren maiztasuna zero ezean ezarrita egonez gero, proba-sorgailua berrabiaraziko du. gelditu gabe funtzionatzen du.

Sorgailuak berrabiarazi hauek beharko ditu bere diseinuagatik; arrazoiren batengatik ezin du 16 segundo baino gehiago irauten duen seinalerik sortu. Kontuan izan behar da bere iraupena 32 biteko zenbaki batek zehazten duela.

Programa osoa honela izango da:

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

Konpilatu dezagun. Ondoren, programa bi ordenagailutan exekutatu daiteke. Edo batean, orain egingo dudan bezala. TShark abiarazten dugu argumentu hauekin:

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

Kontsolaren abiarazte-eremuak harrapaketa hasteari buruzko mezu bat bakarrik bistaratzen badu, seinale ona da - gure ataka ziurrenik beste programa batzuek ez dutela okupatuta esan nahi du. Beste terminal batean, "urruneko" interfono bat simulatuko duen programa-instantzia bat abiarazten dugu ataka-zenbaki hau zehaztuz:

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

Programaren testuan ikus daitekeenez, IP helbide lehenetsia 127.0.0.1 da (loopback lokala).

Beste terminal batean, programaren bigarren instantzia bat abiarazten dugu, gailu lokal bat simulatzen duena. Proba-sorgailu integratua funtzionatzea ahalbidetzen duen argumentu gehigarri bat erabiltzen dugu:

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

Momentu honetan, "urruneko" gailura bidalitako paketeak TShark-ekin kontsolan keinuka hasi behar dira eta ordenagailuaren bozgorailutik etengabeko tonu bat entzungo da.

Dena idatzitako moduan gertatu bada, programaren bigarren kopia berrabiaraziko dugu, baina "β€”gen 440" gako eta argumenturik gabe. Sorgailuaren papera izango duzu orain. Horren ondoren, zarata atera dezakezu mikrofonoan; dagokion soinua entzun beharko zenuke bozgorailuan edo entzungailuetan. Autoekzitazio akustikoa ere gerta daiteke; bozgorailuaren bolumena jaitsi eta efektua desagertu egingo da.

Bi ordenagailutan exekutatu baduzu eta IP helbideei buruz nahastu ez bazenuen, emaitza bera izango duzu zain: bi norabideko kalitate digitaleko ahots komunikazioa.

Hurrengo artikuluan gure iragazkiak - pluginak nola idazten ikasiko dugu, trebetasun honi esker multimedia-streamer-a audio eta bideorako ez ezik, beste arlo zehatz batzuetan ere erabili ahal izango duzu.

Iturria: www.habr.com

Gehitu iruzkin berria