Entdeckt de Mediastreamer2 VoIP-Motor. Deel 9

D'Material vum Artikel ass aus mengem zen Kanal.

Duplex Intercom

Entdeckt de Mediastreamer2 VoIP-Motor. Deel 9

An der leschter Artikel en Duplex Intercom gouf ugekënnegt, an an dësem wäerte mir et maachen.

D'Diagramm gëtt an der Titelbild gewisen. Déi ënnescht Kette vu Filtere bildt den Iwwerdroungswee, dee vun der Tounkaart ufänkt. Et bitt Signal Echantillon aus dem Mikro. Par défaut geschitt dëst mat engem Taux vun 8000 Echantillon pro Sekonn. D'Datebitdéift déi Media Streamer Audio Filtere benotzen ass 16 Bits (dëst ass net wichteg; wann Dir wëllt, kënnt Dir Filtere schreiwen déi mat enger méi héijer Bitdéift funktionnéieren). D'Donnéeën sinn a Blocke vun 160 Proben gruppéiert. Also ass all Block 320 Bytes grouss. Als nächst fidderen mir d'Donnéeën un den Input vum Generator, deen, wann ausgeschalt, "transparent" ass fir d'Donnéeën. Ech hunn et bäigefüügt am Fall wou Dir midd sidd an de Mikrofon ze schwätzen wärend der Debugging - Dir kënnt de Generator benotze fir de Wee mat engem Tonsignal ze "schéissen".

Nom Generator geet d'Signal un den Encoder, deen eis 16-Bit Proben no dem µ-Gesetz (G.711 Standard) an aacht-Bit konvertéiert. Um Ausgang vum Encoder hu mir schonn en Dateblock hallef Gréisst. Am Allgemengen kënne mir Daten ouni Kompressioun iwwerdroen wa mir de Traffic net brauchen. Awer hei ass et nëtzlech fir en Encoder ze benotzen, well Wireshark Audio aus engem RTP Stream nëmmen reproduzéiere kann wann et no dem µ-Gesetz oder engem Gesetz kompriméiert ass.

Nom Encoder ginn déi liicht Blöcke vun Daten an den rtpsend-Filter geschéckt, deen se an engem RTP-Paket setzen, déi néideg Fändelen setzen an se dem Media Streamer ginn fir d'Transmissioun iwwer d'Netzwierk a Form vun engem UDP-Paket.

Déi iewescht Kette vu Filtere bilden den Empfangswee; RTP Pakete, déi vum Medienstreamer aus dem Netz empfaange ginn, ginn an den rtprecv-Filter an, bei der Ausgang vun deem se a Form vun Dateblocken optrieden, jidderee vun deem entsprécht engem kritt Paket. De Block enthält nëmmen Notzlaaschtdaten; am virege Artikel goufen se gréng an der Illustratioun gewisen.

Als nächst ginn d'Blöcke an den Decoderfilter geschéckt, deen d'Single-Byte Proben, déi an hinnen enthale sinn, an linear, 16-Bit konvertéiert. Wat scho vu Media Streamer Filtere veraarbecht ka ginn. An eisem Fall schécken mir se einfach op d'Soundkaart fir op de Spriecher vun Ärem Kopfhörer ze spillen.

Loosst eis elo op d'Softwareimplementatioun weidergoen. Fir dëst ze maachen, kombinéiere mir den Empfänger- a Senderdateien déi mir virdru getrennt hunn. Virun dëser hu mir fix Astellunge fir Ports an Adressen benotzt, awer elo brauche mir de Programm fir d'Astellungen ze benotzen déi mir beim Startup spezifizéieren. Fir dëst ze maachen, wäerte mir Funktionalitéit addéieren fir Kommandozeilargumenter ze veraarbecht. Duerno kënne mir d'IP Adress an den Hafen vum Intercom setzen, mat deem mir eng Verbindung wëllen opbauen.

Als éischt, loosst eis eng Struktur un de Programm derbäisetzen, déi seng Astellunge späichert:

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

typedef struct _app_vars app_vars;

De Programm wäert eng Struktur vun dësem Typ genannt vars deklaréieren.
Als nächst, loosst eis eng Funktioun derbäi fir Kommandozeilargumenter ze analyséieren:

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

Als Resultat vum Parsing ginn d'Argumenter vun der Kommandozeil an de Felder vun der vars Struktur plazéiert. D'Haaptfunktioun vun der Applikatioun ass d'Sendungs- an Empfangsweeër vu Filteren ze sammelen; Nom Uschloss vum Ticker gëtt d'Kontroll op eng onendlech Loop transferéiert, déi, wann d'Generatorfrequenz op Net-Null gesat gouf, den Testgenerator nei starten, sou datt et funktionnéiert ouni opzehalen.

De Generator brauch dës Neistart wéinst sengem Design; aus irgendege Grënn kann et net e Signal produzéieren dat méi wéi 16 Sekonnen dauert. Et sollt bemierkt datt seng Dauer vun enger 32-Bit Zuel spezifizéiert ass.

De ganze Programm wäert esou ausgesinn:

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

Loosst eis kompiléieren. Da kann de Programm op zwee Computer lafen. Oder op engem, wéi ech elo wäert maachen. Mir starten TShark mat de folgenden Argumenter:

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

Wann de Startfeld an der Konsole nëmmen e Message iwwer de Start vun der Erfaassung weist, dann ass dat e gutt Zeechen - et heescht datt eisen Hafen héchstwahrscheinlech net vun anere Programmer besat ass. An engem aneren Terminal lancéiere mir eng Programminstanz déi e "Remote" Intercom simuléiert andeems Dir dës Portnummer spezifizéiert:

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

Wéi kann aus dem Programm Text gesi ginn, ass d'Standard IP Adress 127.0.0.1 (lokal Loopback).

An engem aneren Terminal lancéiere mir eng zweet Instanz vum Programm, deen e lokalen Apparat simuléiert. Mir benotzen en zousätzlecht Argument dat den agebaute Testgenerator erlaabt ze schaffen:

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

Zu dësem Zäitpunkt sollen d'Päckchen, déi op den "Fern" Apparat iwwerdroe ginn, an der Konsole mat TShark ufänken ze blénken, an e kontinuéierleche Toun gëtt vum Computerspeaker héieren.

Wann alles geschitt ass wéi geschriwwe, da restarte mir déi zweet Kopie vum Programm, awer ouni de Schlëssel an Argument "—gen 440". Dir wäert elo d'Roll vum Generator spillen. Duerno kënnt Dir Kaméidi an de Mikro maachen; Dir sollt den entspriechende Klang am Lautsprecher oder Kopfhörer héieren. Akustesch Selbstreizung ka souguer optrieden; schalt de Lautsprechervolumen erof an den Effekt wäert verschwannen.

Wann Dir et op zwee Computere gelaf hutt an net iwwer d'IP Adressen duercherneen sidd, da waart datselwecht Resultat op Iech - zwee-Wee digital Qualitéit Stëmm Kommunikatioun.

Am nächsten Artikel léiere mir wéi Dir eis eege Filtere schreift - Plugins, dank dëser Fäegkeet kënnt Dir de Media Streamer net nëmme fir Audio a Video benotzen, awer och an engem anere spezifesche Beräich.

Source: will.com

Setzt e Commentaire