Матэрыял артыкула ўзяты з майго
Дуплексная перагаворная прылада
У мінулым
Схема намаляваная на загалоўным малюнку. Ніжні ланцуг фільтраў утворыць які перадае тракт, які пачынаецца са гукавой карты. Яна выдае адлікі сігналу з мікрафона. Па змаўчанні гэта адбываецца ў тэмпе 8000 адлікаў за секунду. Разраднасць дадзеных, якую выкарыстоўваюць гукавыя фільтры медыястрымеры - 16 біт (гэта не прынцыпова, пры жаданні можна напісаць фільтры якія будуць працаваць з большай разраднасцю). Дадзеныя згрупаваны ў блокі па 160 адлікаў. Такім чынам, кожны блок мае памер 320 байт. Далей мы падаем дадзеныя на ўваход генератара, які ў выключаным стане "празрысты" для дадзеных. Я яго дадаў на той выпадак, калі вам пры адладцы надакучыць казаць у мікрафон - вы зможаце скарыстацца генератарам, каб "прастрэліць" тракт танальным сігналам.
Пасля генератара сігнал пападае на кодэр, які пераўтворыць нашы 16-бітныя адлікі па µ-закону (стандарт G.711) у васьмібітныя. На выхадзе кодэра мы ўжо маем блок даных у два разы меншага памеру. У агульным выпадку, мы можам перадаваць дадзеныя без сціску, калі нам не трэба эканоміць трафік. Але тут карысна скарыстацца кодэрам, бо Wireshark можа прайграваць гук з RTP-струменю толькі тады калі ён сціснуты па µ-законе ці а-закону.
Пасля кодэра, якія палягчалі блокі дадзеных паступаюць на фільтр 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 (лакальная пятля).
Яшчэ ў адным тэрмінале запускаем другі асобнік праграмы, які імітуе лакальную прыладу. Выкарыстоўваны дадатковы аргумент, які дазваляе працу ўбудаванага тэставага генератара:
$ ./mstest8 --port 7010 --lport 9010 --gen 440
У гэты момант, у кансолі з TShark павінны пачаць мільгаць пакеты перадаюцца ў бок "выдаленай" прылады, а з дынаміка кампутара раздасца бесперапынны танальны сігнал.
Калі ўсё адбылося як па пісаным, то перазапускаем другі асобнік праграмы, але ўжо без ключа і аргументу "-gen 440". Ролю генератара зараз будзеце выконваць вы. Пасля гэтага можна пашумець у мікрафон, у дынаміку ці слухаўках вы павінны пачуць які адпавядае гук. Магчыма нават паўстане акустычнае самаўзбуджэнне, зменшце гучнасць дынаміка і эфект знікне.
Калі ў вы запускалі на двух кампутарах і не заблыталіся ў IP-адрасах, то вас чакае той жа вынік - двухбаковая моўная сувязь лічбавай якасці.
У наступным артыкуле мы навучымся пісаць свае ўласныя фільтры — убудовы, дзякуючы гэтаму навыку вы зможаце прымяніць медыястрымер не толькі для гуку і відэа, але і ў якой-небудзь іншай спецыфічнай вобласці.
Крыніца: habr.com