Materialet i artiklen er taget fra min .
Duplex kommunikationsenhed

I den forrige Der blev annonceret en duplex kommunikationsenhed, og i denne vil vi skabe den.
Diagrammet er vist i titelbilledet. Den nederste filterkæde danner den senderkombination, der begynder med lydkortet. Det udsender signalprøver fra mikrofonen. Som standard sker dette med en hastighed på 8000 prøver pr. sekund. Bitdybden, som lydfiltre i mediestrømmer anvender, er 16 bit (det er ikke påkrævet, hvis ønskes kan man skrive filtre, der arbejder med større dybde). Data er grupperet i blokke på 160 prøver. Således har hver blok en størrelse på 320 byte. Derefter sender vi dataene til indgangen på generatoren, som i slukket tilstand er "gennemsigtig" for data. Jeg har tilføjet den for at give dig mulighed for at bruge generatoren til at "teste" kanalen med en tone, hvis du bliver træt af at tale i mikrofonen under fejlfinding.
Efter generatoren går signalet til encoderen, som konverterer vores 16-bit prøver til otte-bit ved hjælp af µ-law (standard G.711). Ved encoderens udgang har vi allerede en datablok med halvdelen af størrelsen. Generelt kan vi overføre data uden kompression, hvis vi ikke har brug for at spare båndbredde. Men her er det nyttigt at anvende encoder, da Wireshark kun kan afspille lyden fra RTP-strømmen, når den er komprimeret med µ-law eller a-law.
Efter encoderen ankommer de lettede datablokke til rtpsend-filteret, som lægger dem i RTP-pakken, sætter de nødvendige flags og sender dem til mediestrømmeren til transmission over netværket som en UDP-pakke.
Den øverste filterkæde danner modtagerkombinationen; RTP-pakkerne, der modtages af mediestrømmeren fra netværket, går ind i rtprecv-filteret, hvor de vises som datablokke, hver svarende til en modtaget pakke. Blokken indeholder kun nyttelasten, som i den tidligere artikel blev vist med grøn farve.
Derefter ankommer blokke til dekoderens filter, som konverterer de en-byte prøver, der findes i dem, til lineære 16-bit prøver. Disse kan nu behandles af mediestrømmerens filtre. I vores tilfælde sender vi dem simpelthen til lydkortet for at afspille det gennem højttalerne i dit headset.
Lad os nu se på den programmæssige implementering. Til dette vil vi kombinere modtager- og senderfilerne, som vi tidligere har adskilt. Tidligere brugte vi faste indstillinger for porte og adresser, men nu skal programmet kunne bruge de indstillinger, vi angiver ved opstart. Derfor vil vi tilføje funktionalitet til at håndtere kommandolinjeargumenter. Derefter vil vi kunne angive IP-adressen og porten for kommunikationsenheden, som vi ønsker at oprette forbindelse til.
Først tilføjer vi en struktur til programmet, som vil gemme dets indstillinger:
struct _app_vars
{
int local_port; /* Lokal port. */
int remote_port; /* Port for kommunikationsenheden på den fjerne computer. */
char remote_addr[128]; /* IP-adresse på den fjerne computer. */
MSDtmfGenCustomTone dtmf_cfg; /* Indstillinger for testlydgeneratoren. */
};
typedef struct _app_vars app_vars;I programmet vil en struktur af denne type blive erklæret med navnet vars.
Derefter tilføjer vi en funktion til at parse kommandolinjeargumenter:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}Som følge af parseren vil kommandolinjeargumenterne blive placeret i felterne i strukturen vars. Hovedfunktionen i applikationen vil sammenbygge sender- og modtagerruterne ud fra filtre, og efter tilslutningen til tickeren vil kontrollen blive overført til en uendelig løkke, som når generatorens frekvens er sat til ikke-nul, vil genstarte testgeneratoren — så den arbejder uafbrudt.
Disse genstart er nødvendige for generatoren på grund af dens opbygning; af en eller anden grund kan den ikke udgive et signal, der varer længere end 16 sekunder. Det skal bemærkes, at varigheden angives som et 32-bit tal.
Programmet vil i sin helhed se således ud:
/* Файл 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);
}
}Vi kompilerer. Derefter kan programmet køres på to computere. Eller på én, som jeg vil gøre nu. Vi starter TShark med følgende argumenter:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -xHvis der i konsollen kun vises en besked om, at opfangningen er startet, er det et godt tegn - det betyder, at vores port sandsynligvis ikke er optaget af andre programmer. I et andet terminalvindue starter vi et eksempel på programmet, som vil simulere et "fjernt" kommunikationsenhed ved at angive dette portnummer:
$ ./mstest8 --port 9010 --lport 7010Som det fremgår af programmets tekst, bruges IP-adressen 127.0.0.1 (lokal loopback) som standard.
I en anden terminal starter vi et andet eksempel på programmet, der simulerer en lokal enhed. Vi bruger et ekstra argument, der tillader drift af den indbyggede testgenerator:
$ ./mstest8 --port 7010 --lport 9010 --gen 440På dette tidspunkt skal TShark-konsollen begynde at vise pakker sendt mod den "fjerne" enhed, og der vil komme en konstant tonet lyd fra computerens højttaler.
Hvis alt forløb som forventet, genstarter vi det andet eksempel på programmet, men uden nøgle og argumentet "--gen 440". Nu vil du udfylde generatorens rolle. Efter dette kan du lave lyde i mikrofonen; du skal høre en tilsvarende lyd fra højttaleren eller hovedtelefonerne. Det kan endda opstå akustisk feedback, så skru ned for højttalerens lydstyrke for at eliminere effekten.
Hvis du har kørt programmet på to computere uden at forveksle IP-adresserne, vil du få det samme resultat – tovejs talekommunikation af digital kvalitet.
I den næste artikel vil vi lære at skrive vores egne filtre – plugins; med denne færdighed vil du kunne anvende mediestrømmere ikke kun til lyd og video, men også i andre specifikke områder.
Kilde: habr.com
