Artikelmaterialet är hämtat från min .
Duplex samtalsenhet

Tidigare en duplex samtalsenhet har tillkännagivits, och i detta avsnitt kommer vi att demonstrera den.
Diagrammet återges i titeln. Den nedre filterkedjan utgör det sändande kretsverket, som börjar med ljudkortet. Det avger signalprover från mikrofonen. Som standard sker detta i en takt av 8000 prover per sekund. Den datakvalitet som ljudfiltrerna i medieströmaren använder är 16 bitar (detta är inte avgörande, om man vill kan man skriva filter som fungerar med högre kvalitet). Uppgifterna grupperas i block om 160 prover. Därmed har varje block en storlek av 320 byte. Därefter matas uppgifterna till ingången för generatorn, som i avstängt läge är "genomskinlig" för uppgifterna. Jag lade till den för den dagen när du blir trött på att prata i mikrofonen under felsökningen – så att du kan använda generatorn för att "skjuta" kretsen med en tonSignal.
Efter generatorn går signalen till kodaren, som omvandlar våra 16-bitars prover enligt µ-law (standard G.711) till åttabitars. Vid utgången av kodaren har vi redan ett datastort block, som är dubbelt så litet. Generellt kan vi överföra data utan komprimering, om vi inte behöver spara bandbredd. Men här är det användbart att använda kodaren, eftersom Wireshark bara kan återge ljud från RTP-flödet när det är komprimerat enligt µ-law eller a-law.
Efter kodaren kommer de lättade datablokkerna till filtret rtpsend, som packar dem i en RTP-paket, ställer in nödvändiga flaggor och ger dem till medieströmaren för överföring över nätverket som UDP-paket.
Den övre filterkedjan utgör det mottagande kretsverket. RTP-paket som tas emot av medieströmaren från nätverket passerar genom filtret rtprecv, från vilket de framträder som datablokker, där varje block motsvarar ett mottaget paket. Varje block innehåller endast nyttolasten, som i den föregående artikeln visades i grön färg.
Därefter går blocken till dekodern, som omvandlar de enbytes prover i dem till linjära, 16-bitars. Som redan kan bearbetas med medieströmarens filter. I vårt fall ger vi helt enkelt dem till ljudkortet för uppspelning genom högtalarna i ditt headset.
Låt oss nu gå vidare till implementeringen av programmet. För detta kommer vi att kombinera mottagarens och sändarens filer, som vi separerade tidigare. Tidigare använde vi fasta port- och adressinställningar, men nu behöver vi att programmet kan använda de inställningar som vi anger vid starten. Därför kommer vi att lägga till funktioner för att hantera kommandoradsargument. Efter detta kommer vi att kunna ange IP-adressen och porten för kommunikationsenheten vi vill ansluta till.
Först lägger vi till en struktur i programmet som kommer att lagra dess inställningar:
struct _app_vars
{
int local_port; /* Lokal port. */
int remote_port; /* Porten för kommunikationsenheten på den fjärrdatorn. */
char remote_addr[128]; /* IP-adressen för den fjärrdatorn. */
MSDtmfGenCustomTone dtmf_cfg; /* Inställningar för testtonsgeneratorn. */
};
typedef struct _app_vars app_vars;I programmet kommer en struktur av denna typ att deklareras med namnet vars.
Därefter lägger vi till en funktion för att analysera kommandoradsargument:
/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
}
}
}Som ett resultat av analysen kommer kommandoradsargumenten att placeras i fälten i strukturen vars. Huvudfunktionen för applikationen kommer att samla ihop sändnings- och mottagningsbanorna från filter, och efter att ha anslutit en ticker kommer kontrollen att överlämnas till en oändlig loop som, om generatorns frekvens har angivits som icke-noll, kommer att starta om testgeneratorn – så att den kan arbeta utan avbrott.
Dessa omstarter kommer att vara nödvändiga för generatorn på grund av dess konstruktion; av någon anledning kan den inte ge en signal längre än 16 sekunder. Det bör noteras att längden anges med ett 32-bitarsnummer.
Programmet i sin helhet kommer att se ut så här:
/* Файл 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);
}
}Kompilera. Sedan kan programmet köras på två datorer. Eller på en, vilket jag kommer att göra nu. Starta TShark med följande argument:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -xOm bara ett meddelande om att fångsten har påbörjats visas i startfältet i konsolen är det ett gott tecken – det betyder att vår port troligtvis inte är upptagen av andra program. I en annan terminal startar vi ett exempelprogram som kommer att imitera en "fjärr" kommunikationsenhet och ange detta portnummer:
$ ./mstest8 --port 9010 --lport 7010Som syns i programmets text används IP-adressen 127.0.0.1 (lokal loop).
I en annan terminal startar vi en andra instans av programmet som imiterar en lokal enhet. Vi använder ett extra argument som tillåter att den inbyggda testgeneratorn fungerar:
$ ./mstest8 --port 7010 --lport 9010 --gen 440Vid det här tillfället bör paket som sänds till den "avlägsna" enheten börja dyka upp i konsolen med TShark, och från datorns högtalare hörs en kontinuerlig ton.
Om allt har gått som planerat, startar vi den andra instansen av programmet igen, men utan nyckeln och argumentet "—gen 440". Nu kommer du att agera som generator. Efter det kan du göra ljud i mikrofonen; i högtalarna eller hörlurarna bör du höra motsvarande ljud. Det kan till och med uppstå akustisk självväckning, sänk volymen på högtalaren så försvinner effekten.
Om du har startat på två datorer och inte blivit förvirrad av IP-adresserna, kommer du att få samma resultat — tvåvägskommunikation av digital kvalitet.
I nästa artikel kommer vi att lära oss att skriva våra egna filter — plugins, och med den här färdigheten kommer du kunna använda mediastreamern inte bara för ljud och video, utan också inom något annat specifikt område.
Källa: habr.com
