Հոդվածի նյութը վերցված է իմ
դուպլեքս ինտերկոմ
Անցյալում
Դիագրամը ներկայացված է վերնագրի նկարում: Զտիչների ստորին շղթան ձևավորում է փոխանցման ուղին, որը սկսվում է ձայնային քարտից: Այն տրամադրում է ազդանշանի նմուշներ խոսափողից: Լռելյայնորեն դա տեղի է ունենում վայրկյանում 8000 նմուշի արագությամբ: Տվյալների բիթերի խորությունը, որն օգտագործում են մեդիա հոսքի աուդիո զտիչները, 16 բիթ է (սա կարևոր չէ. եթե ցանկանում եք, կարող եք գրել զտիչներ, որոնք կաշխատեն ավելի բարձր բիթային խորությամբ): Տվյալները խմբավորված են 160 նմուշի բլոկների մեջ: Այսպիսով, յուրաքանչյուր բլոկ ունի 320 բայթ չափ: Հաջորդը, մենք տվյալները կերակրում ենք գեներատորի մուտքագրմանը, որը, երբ անջատված է, «թափանցիկ» է տվյալների համար: Ես այն ավելացրեցի, եթե վրիպազերծման ժամանակ հոգնեք խոսափողի հետ խոսելուց, կարող եք օգտագործել գեներատորը՝ երթուղին տոնային ազդանշանով «նկարահանելու» համար:
Գեներատորից հետո ազդանշանը գնում է կոդավորիչ, որը փոխակերպում է մեր 16-բիթանոց նմուշները, համաձայն µ-օրենքի (G.711 ստանդարտ) ութ բիթանոցի: Կոդավորչի ելքում մենք արդեն ունենք կիսով չափ տվյալների բլոկ: Ընդհանուր առմամբ, մենք կարող ենք տվյալներ փոխանցել առանց սեղմման, եթե մեզ հարկավոր չէ խնայել տրաֆիկը: Բայց այստեղ օգտակար է օգտագործել կոդավորիչ, քանի որ Wireshark-ը կարող է վերարտադրել աուդիո RTP հոսքից միայն այն դեպքում, երբ այն սեղմված է μ-օրենքի կամ a-օրենքի համաձայն:
Կոդավորիչից հետո տվյալների ավելի թեթև բլոկները ուղարկվում են rtpsend ֆիլտր, որը դրանք կտեղավորի RTP փաթեթի մեջ, կսահմանի անհրաժեշտ դրոշակները և կտրամադրի դրանք մեդիա հոսքագծին՝ ցանցով փոխանցելու համար UDP փաթեթի տեսքով:
Զտիչների վերին շղթան ձևավորում է ստացման ուղին, ցանցից մեդիա հոսքի կողմից ստացված RTP փաթեթները մտնում են rtprecv ֆիլտր, որի ելքում հայտնվում են տվյալների բլոկների տեսքով, որոնցից յուրաքանչյուրը համապատասխանում է մեկ ստացված փաթեթին: Բլոկը պարունակում է միայն օգտակար բեռնվածքի տվյալներ, նախորդ հոդվածում դրանք նկարազարդում ցուցադրված էին կանաչ գույնով:
Այնուհետև բլոկներն ուղարկվում են ապակոդավորիչի ֆիլտր, որը դրանցում պարունակվող մեկ բայթ նմուշները վերածում է գծային, 16-բիթանոցների: Ինչն արդեն կարող է մշակվել մեդիա հոսքի զտիչներով: Մեր դեպքում մենք դրանք պարզապես ուղարկում ենք ձայնային քարտ՝ ձեր ականջակալի բարձրախոսներով նվագարկելու համար:
Հիմա եկեք անցնենք ծրագրային ապահովման ներդրմանը: Դա անելու համար մենք կմիավորենք ստացողի և հաղորդիչի ֆայլերը, որոնք նախկինում առանձնացրել ենք: Մինչ այս մենք օգտագործում էինք ֆիքսված կարգավորումներ նավահանգիստների և հասցեների համար, բայց այժմ մեզ անհրաժեշտ է, որ ծրագիրը կարողանա օգտագործել այն կարգավորումները, որոնք մենք նշել ենք գործարկման ժամանակ: Դա անելու համար մենք կավելացնենք ֆունկցիոնալություն հրամանի տողի փաստարկների մշակման համար: Որից հետո մենք կկարողանանք սահմանել ինտերկոմի IP հասցեն և պորտը, որի հետ ցանկանում ենք կապ հաստատել։
Նախ, եկեք ծրագրին ավելացնենք կառուցվածք, որը կպահի դրա կարգավորումները.
struct _app_vars
{
int local_port; /* Локальный порт. */
int remote_port; /* Порт переговорного устройства на удаленном компьютере. */
char remote_addr[128]; /* IP-адрес удаленного компьютера. */
MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};
typedef struct _app_vars app_vars;
Ծրագիրը կհայտարարի այս տեսակի կառուցվածքը, որը կոչվում է vars:
Հաջորդը, եկեք մի գործառույթ ավելացնենք հրամանի տողի փաստարկները վերլուծելու համար.
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}
Վերլուծման արդյունքում հրամանի տողի արգումենտները կտեղադրվեն vars կառուցվածքի դաշտերում։ Հավելվածի հիմնական գործառույթը կլինի ֆիլտրերից հաղորդման և ընդունման ուղիները հավաքելը, տիկերը միացնելուց հետո կառավարումը կփոխանցվի անսահման օղակի, որը, եթե գեներատորի հաճախականությունը դրված է ոչ զրոյի, կվերագործարկվի փորձնական գեներատորը, որպեսզի այն աշխատում է առանց կանգ առնելու:
Գեներատորին անհրաժեշտ կլինեն այս վերագործարկումները իր դիզայնի պատճառով, ինչ-ինչ պատճառներով այն չի կարող արտադրել ավելի քան 16 վայրկյան տևող ազդանշան: Հարկ է նշել, որ դրա տեւողությունը նշված է 32 բիթանոց թվով։
Ամբողջ ծրագիրը կունենա հետևյալ տեսքը.
/* Файл 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);
}
}
Եկեք կազմենք. Այնուհետև ծրագիրը կարող է գործարկվել երկու համակարգչի վրա: Կամ մեկի վրա, ինչպես հիմա կանեմ: Մենք գործարկում ենք TShark-ը հետևյալ փաստարկներով.
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x
Եթե վահանակի գործարկման դաշտը ցուցադրում է միայն նկարահանման մեկնարկի մասին հաղորդագրություն, ապա սա լավ նշան է, դա նշանակում է, որ մեր նավահանգիստը, ամենայն հավանականությամբ, զբաղված չէ այլ ծրագրերով: Մեկ այլ տերմինալում մենք գործարկում ենք ծրագրի օրինակ, որը մոդելավորում է «հեռավոր» ինտերկոմը՝ նշելով այս պորտի համարը.
$ ./mstest8 --port 9010 --lport 7010
Ինչպես երևում է ծրագրի տեքստից, լռելյայն IP հասցեն է 127.0.0.1 (local loopback):
Մեկ այլ տերմինալում մենք գործարկում ենք ծրագրի երկրորդ օրինակը, որը մոդելավորում է տեղական սարքը: Մենք օգտագործում ենք լրացուցիչ փաստարկ, որը թույլ է տալիս ներկառուցված թեստային գեներատորին աշխատել.
$ ./mstest8 --port 7010 --lport 9010 --gen 440
Այս պահին «հեռակառավարվող» սարքի վրա փոխանցվող փաթեթները պետք է սկսեն թարթել TSark-ով վահանակում, և համակարգչի բարձրախոսից անընդհատ ձայն կհնչի։
Եթե ամեն ինչ եղել է այնպես, ինչպես գրված է, ապա մենք վերագործարկում ենք ծրագրի երկրորդ օրինակը, բայց առանց «-gen 440» բանալի և արգումենտի: Այժմ դուք կխաղաք գեներատորի դերը: Դրանից հետո դուք կարող եք աղմուկ բարձրացնել խոսափողի մեջ, դուք պետք է լսեք համապատասխան ձայնը բարձրախոսի կամ ականջակալի մեջ: Նույնիսկ կարող է առաջանալ ակուստիկ ինքնագրգռում, իջեցրեք բարձրախոսի ձայնը, և էֆեկտը կվերանա:
Եթե այն գործարկել եք երկու համակարգչով և չեք շփոթել IP հասցեների հետ, ապա ձեզ սպասում է նույն արդյունքը՝ երկկողմանի թվային որակի ձայնային հաղորդակցություն:
Հաջորդ հոդվածում մենք կսովորենք, թե ինչպես գրել մեր սեփական ֆիլտրերը՝ պլագինները, այս հմտության շնորհիվ դուք կկարողանաք օգտագործել մեդիա հոսքը ոչ միայն աուդիո և վիդեո, այլ նաև որոշակի այլ ոլորտում:
Source: www.habr.com