Materiali i artikujt është marrë nga kanali im .
Dispositiv dyal bisedh

Në të kaluarën u njoftohet një dispositiv dyal bisedh i dyanshëm, dhe në këtë do ta realizojmë.
Diagrami është ilustruar në figurën titull. Një zinxhir filtresh të poshtëm formon traktin e transmetimit, i cili fillon nga karta audio. Ajo lëshon mostrat e sinjalit nga mikrofon. Në mënyrë të paracaktuar, kjo ndodh me një ritëm prej 8000 mostrash në sekondë. Të dhënat që përdorin filtrat audio të mediastream-it janë 16-bit (kjo nuk është thelbësore, shpesh mund të shkruhen filtra që do të punojnë me një më shumë përmasë). Të dhënat janë të grupuara në blloqe prej 160 mostrash. Kështu, çdo bllok ka një madhësi prej 320 byte. Më pas, ne i japim të dhënat në hyrje të gjeneratorit, i cili në gjendje të fikur është "transparent" për të dhënat. E kam shtuar atë për rastin kur gjatë debug-imit ju lodh të flisni në mikrofon - do të mund të përdorni gjeneratorin për të "shpuar" traktin me një sinjal tonal.
Pas gjeneratorit, sinjali arrin në koduesin, i cili shndërron mostrat tona 16-bit sipas µ-kanunit (standardi G.711) në tetëbitësh. Në daljen e koduesit, ne tashmë kemi një bllok të dhënash me një madhësi dyfish më të vogël. Në rastin e përgjithshëm, ne mund të transmetojmë të dhënat pa kompresim, nëse nuk na duhet të kursim trafik. Por këtu është e dobishme të përdorim koduesin, pasi Wireshark mund të riprodhojë tingullin nga RTP-fluksi vetëm kur ai është i kompresuar sipas µ-kanunit ose a-kanunit.
Pas koduesit, blloqet e dhënash të lehtësuara hyjnë në filtrin rtpsend, i cili do t'i vendosë në një paketë RTP, vendosë flamujt e nevojshëm dhe i dërgon mediastream-it për t'u transmetuar në rrjet si një paketë UDP.
Zinxhiri i sipërm i filtrave formon traktin e pranimit, RTP-paketat e marra nga mediastream-i nga rrjeti hyjnë në filtrin rtprecv, të cilin dalin tashmë si blloqe të dhënash, secili prej të cilave korrespondon me një paketë të pranuar. Blloku përmban vetëm të dhënat e ngarkesës, në artikullin e kaluar ata u treguan me ngjyrë të gjelbër.
Më pas, blloqet hyjnë në filtrin e dekoduesit, i cili shndërron mostrat një-byte në linear 16-bit. Të cilat tashmë mund të përpunohen nga filtrat e mediastream-it. Në rastin tonë, ne thjesht i japim atyre karta audio, për t'u riprodhuar në altoparlantët e headset-it tuaj.
Tani do të kalojmë në implementimin programor. Për këtë do të bashkojmë skedarët e marrësit dhe dërguesit, të cilët i kemi ndarë më parë. Më parë kemi përdorur parametra të ngurtë për portet dhe adresat, por tani na nevojitet që programi të përdorë ato parametra që ne do të specifikojmë gjatë lançimit. Për këtë do të shtojmë funksionalitetin për trajtimin e argumenteve të linjës së komandës. Pas kësaj do të mund të caktomë adresën IP dhe portin e pajisjes biseduese, me të cilën duam të vendosim lidhjen.
Së pari do të shtojmë në program një strukturë, e cila do të ruajë cilësimet e saj:
struct _app_vars
{
int local_port; /* Porti lokal. */
int remote_port; /* Porti i pajisjes biseduese në kompjuterin e largët. */
char remote_addr[128]; /* Adresa IP e kompjuterit të largët. */
MSDtmfGenCustomTone dtmf_cfg; /* Cilësimet e sinjalit të testimit të gjeneratorit. */
};
typedef struct _app_vars app_vars;Në program do të shpallet një strukturë e këtij lloji me emrin vars.
Pastaj do të shtojmë një funksion për analizimin e argumenteve të linjës së komandës:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}Si rezultat i analizimit, argumentet e linjës së komandës do të vendosen në fushat e strukturës vars. Funksioni kryesor i aplikacionit do të ndërtohet nga filtrat për trashet e dërguesit dhe marrësit, pas lidhjes së tikersit kontrolli do të kalojë në një cikël të pafund, i cili, nëse frekuenca e gjeneratorit është e caktuar ndonjëherë, do të rinstalojë gjeneratorin e testit – që të punojë pa ndalesë.
Këto rinstalime do të jenë të nevojshme për gjeneratorin për shkak të karakteristikave të tij ndërtuese, ndonjëherë ai nuk mund të prodhojë sinjale me kohëzgjatje më të gjatë se 16 sekonda. Njëkohësisht, duhet të theksohet se kohëzgjatja e tij caktohet me një numër 32-bit.
Programi i tërësishëm do të duket kështu:
/* Файл 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);
}
}Kompilojmë. Më pas programi mund të ekzekutohet në dy kompjuterë. Ose në një, siç do ta bëj tani. Nisim TShark me këto argumente:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -xNëse në fushën e lançimit në konsolë shfaqet vetëm mesazhi për fillimin e kapjes, atëherë kjo është një shenjë e mirë - do të thotë që porti ynë me siguri nuk është i zënë nga programe të tjera. Në një terminal tjetër, nisim një instancë të programit, e cila do të imitojë pajisjen biseduese "të largët", duke i caktuar atij këtë numër porte:
$ ./mstest8 --port 9010 --lport 7010Siç duket nga teksti i programit, për default përdoret adresa IP 127.0.0.1 (vija lokale).
Në një terminal tjetër, hapim një ekzemplar të dytë të programit, i cili imiton një pajisje lokale. Përdorim një argument shtesë, i cili lejon punën e gjeneruesit të testit të ndërtuar:
$ ./mstest8 --port 7010 --lport 9010 --gen 440Në këtë moment, në konzolën me TShark, duhet të fillojnë të shfaqen paketat e dërguara në drejtim të pajisjes "të largët", dhe nga altoparlanti i kompjuterit do të dëgjohet një sinjal tonal i vazhdueshëm.
Nëse gjithçka ndodhi ashtu siç duhet, rinisni ekzemplarin e dytë të programit, por tashmë pa çelësin dhe argumentin "—gen 440". Tani do të luani rolin e gjeneratorit. Pas kësaj, mund të bëni zhurmë në mikrofon, në altoparlant apo në kufje duhet të dëgjoni tingullin përkatës. Mund të ndodhë madje një vetë-ngacmim akustik, ulni volumet e altoparlantit dhe efekti do të ndalet.
Nëse keni filluar në dy kompjuterë dhe nuk keni ndërlikuar adresat IP, ju pret e njëjta rezultat — komunikim dy-sided me cilësi digjitale.
Në artikullin e ardhshëm do të mësojmë si të shkruajmë filtrom të tanishëm — plugina, falë këtij aftësie do të mund të aplikoni mediastreamer jo vetëm për zë dhe video, por edhe në ndonjë fushë tjetër specifike.
Burimi: habr.com
