A cikk anyaga az én
Duplex kaputelefon
Az utolsóban
A diagram a cím ábrán látható. A szűrők alsó lánca alkotja az átviteli utat, amely a hangkártyától indul. Jelmintákat biztosít a mikrofonból. Ez alapértelmezés szerint 8000 minta/másodperc sebességgel történik. A média streamer hangszűrői által használt adatbitmélység 16 bit (ez nem fontos; ha szeretné, írhat olyan szűrőket, amelyek nagyobb bitmélységgel működnek). Az adatok 160 mintát tartalmazó blokkokba vannak csoportosítva. Így minden blokk 320 bájt méretű. Ezután az adatokat a generátor bemenetére tápláljuk, amely kikapcsolt állapotban „átlátszó” az adatok számára. Hozzátettem arra az esetre, ha megunnád, hogy hibakeresés közben beleszólsz a mikrofonba - a generátor segítségével hangjelzéssel „lelövik” az utat.
A generátor után a jel a kódolóhoz kerül, amely a µ-törvény szerint (G.16 szabvány) nyolcbitessé alakítja át a 711 bites mintáinkat. A kódoló kimenetén már van egy feleakkora adatblokk. Általánosságban elmondható, hogy tömörítés nélkül is továbbíthatunk adatokat, ha nem kell forgalmat spórolnunk. De itt hasznos kódolót használni, mivel a Wireshark csak akkor képes reprodukálni az RTP-folyamból származó hangot, ha az a µ-törvény vagy a-törvény szerint van tömörítve.
A kódoló után a könnyebb adatblokkok az rtpsend szűrőbe kerülnek, amely RTP csomagba helyezi őket, beállítja a szükséges jelzőket és átadja a média streamernek, hogy UDP csomag formájában továbbítsák a hálózaton.
A szűrők felső lánca képezi a vételi útvonalat, a média streamer által a hálózatról kapott RTP csomagok az rtprecv szűrőbe kerülnek, amelynek kimenetén adatblokkok formájában jelennek meg, amelyek mindegyike egy-egy fogadott csomagnak felel meg. A blokk csak hasznos adatot tartalmaz, az előző cikkben ezek az ábrán zölddel voltak láthatók.
Ezután a blokkokat a dekódoló szűrőhöz küldik, amely a bennük lévő egybájtos mintákat lineáris, 16 bitesekké alakítja. Amit már a média streamer szűrői is fel tudnak dolgozni. Esetünkben egyszerűen elküldjük őket a hangkártyára, hogy lejátszhassák a headset hangszóróin.
Most térjünk át a szoftver implementációjára. Ehhez egyesítjük a korábban szétválasztott vevő és adó fájlokat. Korábban fix beállításokat használtunk a portokhoz és címekhez, de most már szükségünk van arra, hogy a program az indításkor megadott beállításokat tudja használni. Ehhez adnánk hozzá a parancssori argumentumok feldolgozásához szükséges funkciókat. Ez után tudjuk majd beállítani annak a kaputelefonnak az IP címét és portját, amellyel kapcsolatot szeretnénk létesíteni.
Először adjunk hozzá egy szerkezetet a programhoz, amely tárolja a beállításait:
struct _app_vars
{
int local_port; /* Локальный порт. */
int remote_port; /* Порт переговорного устройства на удаленном компьютере. */
char remote_addr[128]; /* IP-адрес удаленного компьютера. */
MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};
typedef struct _app_vars app_vars;
A program deklarál egy ilyen típusú, vars nevű struktúrát.
Ezután adjunk hozzá egy függvényt a parancssori argumentumok elemzéséhez:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}
Az elemzés eredményeként a parancssori argumentumok a vars struktúra mezőibe kerülnek. Az alkalmazás fő funkciója az adási és vételi utak összegyűjtése lesz a szűrőktől, a ticker csatlakoztatása után a vezérlés egy végtelen hurokra kerül át, amely ha a generátor frekvenciáját nem nullára állítottuk, újraindítja a tesztgenerátort, így megállás nélkül működik.
A generátornak a kialakítása miatt szüksége lesz ezekre az újraindításokra, valamiért nem tud 16 másodpercnél tovább tartó jelet produkálni. Megjegyzendő, hogy időtartamát egy 32 bites szám határozza meg.
A teljes program így fog kinézni:
/* Файл 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);
}
}
Összeállítjuk. Ekkor a program két számítógépen is futtatható. Vagy az egyiken, ahogy most fogom. A TSharkot a következő érvekkel indítjuk el:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x
Ha a konzol indítómezője csak a rögzítés kezdetére vonatkozó üzenetet jeleníti meg, akkor ez jó jel - ez azt jelenti, hogy a portunkat valószínűleg nem foglalják el más programok. Egy másik terminálon elindítunk egy programpéldányt, amely egy „távoli” kaputelefont szimulál a következő portszám megadásával:
$ ./mstest8 --port 9010 --lport 7010
Amint a program szövegéből kiderül, az alapértelmezett IP-cím 127.0.0.1 (helyi hurok).
Egy másik terminálon elindítjuk a program második példányát, amely egy helyi eszközt szimulál. Egy további argumentumot használunk, amely lehetővé teszi a beépített tesztgenerátor működését:
$ ./mstest8 --port 7010 --lport 9010 --gen 440
Ebben a pillanatban a „távoli” eszköz felé továbbított csomagoknak villogni kell a TShark konzolban, és folyamatos hang hallható a számítógép hangszórójából.
Ha minden a leírtak szerint történt, akkor újraindítjuk a program második példányát, de a „—gen 440” kulcs és argumentum nélkül. Most a generátor szerepét fogod játszani. Ezt követően zajt üthet a mikrofonba, a megfelelő hangot a hangszóróban vagy a fejhallgatóban kell hallania. Akusztikus öngerjesztés is előfordulhat; csökkentse a hangszóró hangerejét, és a hatás eltűnik.
Ha két számítógépen futtatta, és nem keveredett össze az IP-címekkel, akkor ugyanaz az eredmény vár Önre - kétirányú digitális minőségű hangkommunikáció.
A következő cikkben megtanuljuk, hogyan írjunk saját szűrőket - bővítményeket, ennek a készségnek köszönhetően nem csak hangra és videóra használhatod a média streamert, hanem más speciális területeken is.
Forrás: will.com