U materiale di l'articulu hè pigliatu da u mo
Intercom duplex
In l'ultimu
U diagramma hè mostratu in a figura di titulu. A catena più bassa di filtri forma a strada di trasmissione, chì parte da a carta di sonu. Fornisce campioni di signale da u microfonu. Per automaticamente, questu accade à una tarifa di 8000 campioni per seconda. A prufundità di bit di dati chì i filtri audio media streamer usanu hè 16 bits (questu ùn hè micca impurtante; se vulete, pudete scrive filtri chì funzionanu cù una prufundità di bit più altu). I dati sò raggruppati in blocchi di 160 campioni. Cusì, ogni bloccu hè 320 bytes in grandezza. In seguitu, avemu alimentatu i dati à l'input di u generatore, chì, quandu si spegne, hè "trasparente" à i dati. L'aghju aghjustatu in casu chì vi stancu di parlà in u micru durante a debugging - pudete aduprà u generatore per "sparà" a strada cù un signalu di tonu.
Dopu à u generatore, u signale passa à l'encoder, chì cunverte i nostri campioni di 16 bits secondu a lege µ (standard G.711) in ottu bit. À a pruduzzioni di l'encoder, avemu digià un bloccu di dati a mità di a dimensione. In generale, pudemu trasmette dati senza cumpressione si ùn avemu micca bisognu di salvà u trafficu. Ma quì hè utile aduprà un codificatore, postu chì Wireshark pò ripruduce l'audio da un flussu RTP solu quandu hè cumpressu secondu a lege µ o a lege.
Dopu à l'encoder, i blocchi più ligeri di dati sò mandati à u filtru rtpsend, chì i mette in un pacchettu RTP, stabilisce i bandieri necessarii è dà à u media streamer per a trasmissione nantu à a reta in forma di un pacchettu UDP.
A catena superiore di i filtri forma u percorsu di ricezione; i pacchetti RTP ricevuti da u media streamer da a reta entranu in u filtru rtprecv, à l'output di quale appariscenu in forma di blocchi di dati, ognunu di i quali currisponde à un pacchettu ricevutu. U bloccu cuntene solu dati di carica; in l'articulu precedente sò stati mostrati in verde in l'illustrazione.
In seguitu, i blocchi sò mandati à u filtru di decodificatore, chì cunverta i campioni di un byte cuntenuti in elli in lineari, 16-bit. Chì pò digià esse trattatu da filtri media streamer. In u nostru casu, simpricimenti li mandemu à a carta di sonu per a riproduzione nantu à i parlanti di i vostri cuffie.
Avà andemu à l'implementazione di u software. Per fà questu, combineremu i schedarii di ricevitore è trasmettitore chì avemu separatu prima. Prima di questu, avemu usatu paràmetri fissi per i porti è l'indirizzi, ma avà avemu bisognu di u prugramma per pudè utilizà i paràmetri chì avemu specificatu à l'iniziu. Per fà questu, aghjustemu a funziunalità per processà l'argumenti di a linea di cummanda. Dopu à quale pudemu stabilisce l'indirizzu IP è u portu di l'intercom cù quale vulemu stabilisce una cunnessione.
Prima, aghjustemu una struttura à u prugramma chì guardà i so paràmetri:
struct _app_vars
{
int local_port; /* Локальный порт. */
int remote_port; /* Порт переговорного устройства на удаленном компьютере. */
char remote_addr[128]; /* IP-адрес удаленного компьютера. */
MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};
typedef struct _app_vars app_vars;
U prugramma dichjarà una struttura di stu tipu chjamata vars.
In seguitu, aghjustemu una funzione per analizà l'argumenti di a linea di cummanda:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}
In u risultatu di l'analisi, l'argumenti di a linea di cumanda seranu posti in i campi di a struttura vars. A funzione principale di l'applicazione serà di cullà i percorsi di trasmissione è di ricezione da i filtri; dopu avè cunnessu u ticker, u cuntrollu serà trasferitu à un ciclu infinitu chì, se a frequenza di u generatore hè stata impostata à micca zero, riavviarà u generatore di prova in modu chì funziona senza piantà.
U generatore avarà bisognu di sti riavvii per via di u so disignu; per una certa ragione ùn pò micca pruduce un signalu chì dura più di 16 seconde. Si deve esse nutatu chì a so durata hè specificatu da un numeru 32-bit.
Tuttu u prugramma sarà cusì:
/* Файл 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);
}
}
Cumpilemu. Allora u prugramma pò esse eseguitu nantu à dui computer. O nantu à unu, cum'è aghju da fà avà. Lancemu TShark cù i seguenti argumenti:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x
Se u campu di lanciamentu in a cunsola mostra solu un missaghju annantu à l'iniziu di a cattura, allora questu hè un bonu signu - significa chì u nostru portu ùn hè micca prubabilmente occupatu da altri prugrammi. In un altru terminal, lancemu una istanza di prugramma chì simulerà un intercom "remotu" specificendu stu numeru di portu:
$ ./mstest8 --port 9010 --lport 7010
Comu pò esse vistu da u testu di u prugramma, l'indirizzu IP predeterminatu hè 127.0.0.1 (loopback locale).
In un altru terminal, lanciamu una seconda istanza di u prugramma, chì simula un dispositivu lucale. Utilizemu un argumentu supplementu chì permette à u generatore di teste integratu per travaglià:
$ ./mstest8 --port 7010 --lport 9010 --gen 440
À questu mumentu, i pacchetti trasmessi versu u dispositivu "remotu" deve cumincià à lampassi in a cunsola cù TShark, è un tonu cuntinuu serà intesu da u parlante di l'urdinatore.
Se tuttu hè accadutu cum'è scrittu, allora ripigliamu a seconda copia di u prugramma, ma senza a chjave è l'argumentu "-gen 440". Avete avà ghjucà u rolu di generatore. Dopu questu, pudete fà u sonu in u micrufonu; duvete sente u sonu currispondente in u parlante o l'auriculare. L'auto-eccitazione acustica pò ancu esse; diminuite u voluminu di u parlante è l'effettu sparirà.
Se l'avete currettu in dui computers è ùn avete micca cunfunditu nantu à l'indirizzi IP, allora u listessu risultatu vi aspetta - cumunicazione vocale di qualità digitale bidirezionale.
In u prossimu articulu avemu da amparà à scrive i nostri filtri - plugins, grazia à sta cumpetenza, puderà utilizà u media streamer micca solu per l'audio è u video, ma ancu in qualchì altra zona specifica.
Source: www.habr.com