Materiál článku je prevzatý z môjho .
Duplexné komunikačné zariadenie

V minulosti Bolo ohlásené duplexné komunikačné zariadenie, a v tejto časti ho vytvoríme.
Schéma je znázornená na úvodnom obrázku. Dolný filter tvoria prenosový trakt, ktorý začína so zvukovou kartou. Vydáva odhady signálu z mikrofónu. Predvolene sa to deje pri tempe 8000 odhadov za sekundu. Hĺbka údajov, ktorú používajú zvukové filtre mediastreamera, je 16 bitov (to nie je zásadné, ak si želáte, môžete napísať filtre, ktoré budú pracovať s vyššou hĺbkou). Údaje sú rozdelené do blokov po 160 odhadoch. Takže každý blok má veľkosť 320 bajtov. Potom predávame údaje na vstup generátora, ktorý v vypnutom stave je pre údaje "priehľadný". Pridal som ho pre prípad, keď sa vám pri ladení unaví hovoriť do mikrofónu — môžete využiť generátor na "prehrať" trakt tónovým signálom.
Po generátore signál prechádza do kodéra, ktorý prevádza naše 16-bitové odhady podľa µ-zákona (štandard G.711) na osembitové. Na výstupe kodéra už máme blok údajov v dvojnásobne menšej veľkosti. Všeobecne môžeme prenášať údaje bez kompresie, ak nepotrebujeme šetriť prenos. Ale tu je užitočné využiť kodér, pretože Wireshark môže reprodukovať zvuk z RTP-potoku iba vtedy, keď je komprimovaný podľa µ-zákona alebo a-zákona.
Po kodéri sa zjednodušené bloky údajov dostávajú na filter rtpsend, ktorý ich vloží do RTP-balenia, nastaví potrebné vlajky a odovzdá ich mediastreamerovi na prenos po sieti vo forme UDP-balenia.
Horná časť filtrovania tvorí prijímací trakt, RTP-balenia, ktoré mediastreamer prijíma zo siete, vstupujú do filtra rtprecv, na výstupe ktorého sa zobrazujú ako bloky údajov, z ktorých každý zodpovedá jednému prijatému baleniu. Blok obsahuje iba údaje užitočnej zaťaženia, v predchádzajúcom článku boli znázornené zelenou farbou.
Ďalej bloky prechádzajú do dekódera, ktorý prevádza obsiahnuté jednorázové odhady na lineárne, 16-bitové. Tie už môžeme spracovávať filtrami mediastreamera. V našom prípade ich jednoducho odovzdáme na zvukovú kartu na prehrávanie na reproduktoroch vašej súpravy.
Teraz prejdeme na programovú implementáciu. Na to spojíme súbory prijímača a vysielača, ktoré sme predtým oddelili. Predtým sme používali pevne nastavené porty a adresy, ale teraz chceme, aby program mohol použiť nastavenia, ktoré určíme pri spustení. Na to pridáme funkciu na spracovanie argumentov príkazového riadku. Potom budeme môcť určiť IP adresu a port vyjednávacieho zariadenia, s ktorým chceme nadviazať spojenie.
Najprv pridáme do programu štruktúru, ktorá bude uchovávať jeho nastavenia:
struct _app_vars
{
int local_port; /* Lokálny port. */
int remote_port; /* Port vyjednávacieho zariadenia na vzdialenom počítači. */
char remote_addr[128]; /* IP adresa vzdialeného počítača. */
MSDtmfGenCustomTone dtmf_cfg; /* Nastavenia testovacieho signálu generátora. */
};
typedef struct _app_vars app_vars;V programe bude vyhlásená štruktúra tohto typu s názvom vars.
Potom pridáme funkciu na analýzu argumentov príkazového riadku:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}Ako výsledok analýzy, argumenty príkazového riadku budú umiestnené do polí štruktúry vars. Hlavná funkcia aplikácie bude zbierať z filtrov prenosové a prijímacie trasy, po pripojení tickeru bude riadenie prevedené na nekonečnú slučku, ktorá, ak bude frekvencia generátora zadaná ako nenulová, reštartuje testovací generátor — aby pracoval nepretržite.
Tieto reštarty budú potrebné generátorovi kvôli jeho vlastnostiam, z nejakého dôvodu nemôže vydávať signál dlhší ako 16 sekúnd. Pri tom treba poznamenať, že dĺžka je zadávaná 32-bitovým číslom.
Program v celku bude vyzerať takto:
/* Файл 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);
}
}Kompilujeme. Potom môže program spustiť na dvoch počítačoch. Alebo na jednom, ako to budem robiť teraz. Spúšťame TShark s nasledujúcimi argumentmi:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -xAk sa v okne konzoly objaví len správa o začiatku zachytávania, je to dobré znamenie — znamená to, že náš port pravdepodobne nie je obsadený inými programami. V inom termináli spúšťame záznam programu, ktorý bude simulovať "vzdialené" vyjednávacie zariadenie, pričom mu uvedieme toto číslo portu:
$ ./mstest8 --port 9010 --lport 7010Ako vidíte z textu programu, predvolene sa používa IP adresa 127.0.0.1 (lokálna slučka).
Na ďalšom termináli spustíme druhý exemplár aplikácie, ktorý imituje miestne zariadenie. Použijeme ďalší argument, ktorý povoľuje prácu zabudovaného testovacieho generátora:
$ ./mstest8 --port 7010 --lport 9010 --gen 440V tomto momente by sa v konzole s TSharkom mali začať objavovať pakety prenášané smerom k "ďalekému" zariadení, a z reproduktora počítača sa ozve nepretržitý tonálny signál.
Ak sa všetko udialo ako má, tak reštartujeme druhý exemplár aplikácie, ale už bez kľúča a argumentu "--gen 440". Úlohu generátora teraz budete plniť vy. Potom môžete trochu zakričať do mikrofónu, v reproduktore alebo slúchadlách by ste mali počuť zodpovedajúci zvuk. Môže dokonca nastať akustická samovzbudzovanie, znížte hlasitosť reproduktora a efekt zmizne.
Ak ste spustili na dvoch počítačoch a nezamieňali ste si IP adresy, čaká vás rovnaký výsledok — obojsmerná hlasová komunikácia digitálnej kvality.
V nasledujúcom článku sa naučíme písať svoje vlastné filtre — pluginy, vďaka tomuto zručnosti budete môcť použiť mediastreamer nielen pre zvuk a video, ale aj v inej špecifickej oblasti.
Zdroj: habr.com
