La materialo de la artikolo estas prenita el mia .
Dupleksa pordotelefono

En la lasta Dupleksa pordotelefono estis anoncita, kaj en ĉi tiu ni faros ĝin.
La diagramo estas montrita en la titolfiguro. La malsupra filtrilĉeno formas la dissendan vojon, kiu komenciĝas per la sonkarto. Ĝi eligas signalajn specimenojn el la mikrofono. Defaŭlte, tio okazas je rapideco de 8000 specimenoj po sekundo. La datumrezolucio uzata de la aŭdiofiltriloj de la multmedia fluilo estas 16 bitoj (tio ne estas kritika; filtriloj, kiuj funkcias kun pli alta rezolucio, povas esti skribitaj laŭdezire). La datumoj estas grupigitaj en blokojn de 160 specimenoj, farante ĉiun blokon 320 bajtojn granda. Poste, ni sendas la datumojn al la generatora enigo, kiu estas "travidebla" al la datumoj kiam malŝaltita. Mi aldonis tion por ke se vi laciĝas paroli en la mikrofonon dum cimigo, vi povu uzi la generatoron por "pafi" tonon tra la vojo.
Post la generatoro, la signalo iras al la kodigilo, kiu konvertas niajn 16-bitajn specimenojn uzante µ-leĝon (la G.711-normo) en 8-bitajn. Ĉe la eliro de la kodigilo, ni nun havas datenblokon duonon de la grandeco. Ĝenerale, ni povas sendi datumojn sen kunpremo se ni ne bezonas ŝpari bendlarĝon. Tamen, uzi kodigilon estas utila ĉi tie, ĉar Wireshark povas ludi aŭdion nur de RTP-fluo se ĝi estas kunpremita uzante µ-leĝon aŭ α-leĝon.
Post la kodigilo, la pli malpezaj datenblokoj estas senditaj al la rtpsend-filtrilo, kiu metos ilin en RTP-pakaĵeton, agordos la necesajn flagojn, kaj pasos ilin al la multmedia fluilo por dissendo tra la reto kiel UDP-pakaĵeto.
La supra filtrilĉeno formas la ricevan vojon. RTP-pakaĵetoj ricevitaj de la multmedia fluilo de la reto estas sendataj al la rtprecv-filtrilo, ĉe kies eliro ili aperas kiel datenblokoj, ĉiu respondante al unuopa ricevita pakaĵeto. Bloko enhavas nur la utilajn datumojn, kiuj estis montritaj verde en la ilustraĵo de la antaŭa artikolo.
La blokoj estas poste sendataj al la malĉifrila filtrilo, kiu konvertas la unu-bajtajn specimenojn, kiujn ili enhavas, en liniajn 16-bitajn datumojn. Ĉi tiuj povas esti prilaboritaj per la filtriloj de la multmedia fluigilo. En nia kazo, ni simple sendas ilin al la sonkarto por reludigo per la laŭtparoliloj de via kapaŭskultilo.
Nun ni transiru al la programara efektivigo. Por fari tion, ni kombinos la ricevilan kaj sendilan dosierojn, kiujn ni antaŭe apartigis. Antaŭe, ni uzis fiksajn pordajn kaj adresajn agordojn, sed nun ni bezonas, ke la programo povu uzi la agordojn, kiujn ni specifas ĉe la starto. Por fari tion, ni aldonos funkciojn por prilabori komandliniajn argumentojn. Post tio, ni povos specifi la IP-adreson kaj pordon de la interparolilo, kun kiu ni volas komuniki.
Unue, ni aldonu strukturon al la programo, kiu konservos ĝiajn agordojn:
struct _app_vars
{
int local_port; /* Локальный порт. */
int remote_port; /* Порт переговорного устройства на удаленном компьютере. */
char remote_addr[128]; /* IP-адрес удаленного компьютера. */
MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};
typedef struct _app_vars app_vars;La programo deklaros strukturon de ĉi tiu tipo nomitan vars.
Poste ni aldonos funkcion por analizi komandliniajn argumentojn:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}Post la sintakso, la argumentoj de la komandlinio estos metitaj en la kampojn de la strukturo "vars". La ĉefa funkcio de la aplikaĵo kunmetos la sendo- kaj ricevo-padojn el la filtriloj. Post konekto de la teletajpilo, la kontrolo estos transdonita al senfina buklo, kiu, se la frekvenco de la generatoro estis agordita al ne-nula valoro, rekomencos la testgeneratoron tiel ke ĝi funkcias kontinue.
Tiuj rekomencoj estos necesaj por la generatoro pro ĝia dezajno; pro iu kialo, ĝi ne povas produkti signalon pli longan ol 16 sekundojn. Notindas, ke ĝia daŭro estas specifita per 32-bita nombro.
La tuta programo aspektos jene:
/* Файл 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);
}
}Ni kompilu. Poste, vi povas ruli la programon sur du komputiloj. Aŭ sur unu, kiel mi faros nun. Lanĉu TShark kun la jenaj argumentoj:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -xSe la startiga kampo de la konzolo montras nur mesaĝon pri la komenco de la kapto, tio estas bona signo — ĝi signifas, ke nia pordo verŝajne estas neokupita de aliaj programoj. En alia terminalo, lanĉu instancon de la programo, kiu simulos "malproksiman" interparolan aparaton per specifado de ĉi tiu pordnumero:
$ ./mstest8 --port 9010 --lport 7010Kiel videblas el la programteksto, la defaŭlta IP-adreso uzata estas 127.0.0.1 (loka buklo).
En alia terminalo, ni lanĉas duan instancon de la programo, kiu simulas lokan aparaton. Ni uzas plian argumenton, kiu ebligas la enkonstruitan testgeneratoron:
$ ./mstest8 --port 7010 --lport 9010 --gen 440Je ĉi tiu punkto, pakaĵetoj senditaj al la "malproksima" aparato devus komenci fulmi en la TShark-konzolo, kaj kontinua tona signalo estos aŭdita el la komputila laŭtparolilo.
Se ĉio iris laŭplane, rekomencu la duan instancon de la programo, sed sen la klavo kaj argumento "--gen 440". Vi nun agos kiel la generatoro. Post tio, vi povas fari iom da bruo en la mikrofonon; vi devus aŭdi respondan sonon en la laŭtparolilo aŭ aŭdiloj. Vi eĉ povus sperti akustikan mem-ekscitiĝon; malaltigi la laŭtparolilan laŭtvoĉon forigos la efikon.
Se vi funkciigis ĝin sur du komputiloj kaj ne fuŝis la IP-adresojn, vi ricevos la saman rezulton — dudirektan, cifereckvalitan voĉkomunikadon.
En la sekva artikolo, ni lernos kiel verki viajn proprajn filtrilojn — kromaĵojn. Ĉi tiu kapablo ebligos al vi uzi la multmedian fluilon ne nur por aŭdio kaj video, sed ankaŭ en aliaj specifaj areoj.
fonto: www.habr.com
