Artikuluaren materiala niretik hartua da
duplex interfonoa
Azkenean
Diagrama izenburuko irudian ageri da. Beheko iragazki-kateak transmisio-bidea osatzen du, soinu-txarteletik abiatzen dena. Mikrofonotik seinale-laginak eskaintzen ditu. Lehenespenez, segundoko 8000 laginko abiaduran gertatzen da. Media streamer audio-iragazkiak erabiltzen duten datu-bit-sakonera 16 bitekoa da (hau ez da garrantzitsua; nahi izanez gero, bit-sakonera handiagoarekin funtzionatuko duten iragazkiak idatz ditzakezu). Datuak 160 laginetako blokeetan biltzen dira. Horrela, bloke bakoitzak 320 byte ditu. Ondoren, datuak sorgailuaren sarrerara elikatzen ditugu, eta hori, itzalita dagoenean, datuekiko "gardena" da. Araztean mikrofonoarekin hitz egiteaz nekatzen bazara gehitu dut: sorgailua erabil dezakezu tonu seinale batekin bidea "tiro egiteko".
Sorgailuaren ondoren, seinalea kodegailura doa eta honek Β΅-legearen arabera (G.16 estandarra) gure 711 biteko laginak zortzi bitekotan bihurtzen ditu. Kodetzailearen irteeran, dagoeneko tamaina erdiaren datu-bloke bat dugu. Oro har, datuak konpresiorik gabe transmiti ditzakegu trafikoa aurreztu behar ez badugu. Baina hemen erabilgarria da kodetzailea erabiltzea, izan ere, Wireshark-ek RTP korronte batetik audioa erreproduzi dezake Β΅-legearen edo a-legearen arabera konprimitzen denean soilik.
Kodetzailearen ondoren, datu-bloke arinagoak rtpsend iragazkira bidaltzen dira, RTP pakete batean sartuko ditu, beharrezko banderak ezarri eta multimedia streamer-ari emango dizkio sarean bidaltzeko UDP pakete moduan.
Iragazkien goiko kateak eratzen du jasotzeko bidea; saretik multimedia-streamer-ek jasotako RTP paketeak rtprecv iragazkia sartzen dira, eta irteeran datu-bloke moduan agertzen dira, bakoitza jasotako pakete bati dagokio. Blokeak karga-datuak soilik ditu; aurreko artikuluan ilustrazioan berdez agertzen ziren.
Ondoren, blokeak deskodetzaile-iragazkira bidaltzen dira, eta haietan dauden byte bakarreko laginak 16 biteko linealetan bihurtzen ditu. Dagoeneko multimedia-streamer-iragazkiek prozesatu dezaketena. Gure kasuan, soinu-txartelera bidaltzen ditugu entzungailuen bozgorailuetan erreproduzitzeko.
Orain pasa gaitezen softwarearen ezarpenera. Horretarako, aurretik bereizi ditugun hartzaile eta igorle fitxategiak konbinatuko ditugu. Aurretik, portuetarako eta helbideetarako ezarpen finkoak erabiltzen genituen, baina orain programa behar dugu abiaraztean zehazten ditugun ezarpenak erabili ahal izateko. Horretarako, komando lerroko argumentuak prozesatzeko funtzionalitateak gehituko genituzke. Horren ostean, konexio bat ezarri nahi dugun interfonoaren IP helbidea eta ataka ezarri ahal izango ditugu.
Lehenik eta behin, gehi diezaiogun bere ezarpenak gordeko dituen programari egitura bat:
struct _app_vars
{
int local_port; /* ΠΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΏΠΎΡΡ. */
int remote_port; /* ΠΠΎΡΡ ΠΏΠ΅ΡΠ΅Π³ΠΎΠ²ΠΎΡΠ½ΠΎΠ³ΠΎ ΡΡΡΡΠΎΠΉΡΡΠ²Π° Π½Π° ΡΠ΄Π°Π»Π΅Π½Π½ΠΎΠΌ ΠΊΠΎΠΌΠΏΡΡΡΠ΅ΡΠ΅. */
char remote_addr[128]; /* IP-Π°Π΄ΡΠ΅Ρ ΡΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΡΡΡΠ΅ΡΠ°. */
MSDtmfGenCustomTone dtmf_cfg; /* ΠΠ°ΡΡΡΠΎΠΉΠΊΠΈ ΡΠ΅ΡΡΠΎΠ²ΠΎΠ³ΠΎ ΡΠΈΠ³Π½Π°Π»Π° Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°. */
};
typedef struct _app_vars app_vars;
Programak vars izeneko mota honetako egitura bat aldarrikatuko du.
Ondoren, gehi dezagun funtzio bat komando-lerroko argumentuak analizatzeko:
/* Π€ΡΠ½ΠΊΡΠΈΡ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΡ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ² ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ Π²
* Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ. */
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]);
}
}
}
Analisiaren ondorioz, komando-lerroko argumentuak vars egiturako eremuetan jarriko dira. Aplikazioaren funtzio nagusia iragazkietatik transmititzeko eta jasotzeko bideak biltzea izango da; ticker-a konektatu ondoren, kontrola begizta infinitu batera transferituko da eta, sorgailuaren maiztasuna zero ezean ezarrita egonez gero, proba-sorgailua berrabiaraziko du. gelditu gabe funtzionatzen du.
Sorgailuak berrabiarazi hauek beharko ditu bere diseinuagatik; arrazoiren batengatik ezin du 16 segundo baino gehiago irauten duen seinalerik sortu. Kontuan izan behar da bere iraupena 32 biteko zenbaki batek zehazten duela.
Programa osoa honela izango da:
/* Π€Π°ΠΉΠ» 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);
}
}
Konpilatu dezagun. Ondoren, programa bi ordenagailutan exekutatu daiteke. Edo batean, orain egingo dudan bezala. TShark abiarazten dugu argumentu hauekin:
$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x
Kontsolaren abiarazte-eremuak harrapaketa hasteari buruzko mezu bat bakarrik bistaratzen badu, seinale ona da - gure ataka ziurrenik beste programa batzuek ez dutela okupatuta esan nahi du. Beste terminal batean, "urruneko" interfono bat simulatuko duen programa-instantzia bat abiarazten dugu ataka-zenbaki hau zehaztuz:
$ ./mstest8 --port 9010 --lport 7010
Programaren testuan ikus daitekeenez, IP helbide lehenetsia 127.0.0.1 da (loopback lokala).
Beste terminal batean, programaren bigarren instantzia bat abiarazten dugu, gailu lokal bat simulatzen duena. Proba-sorgailu integratua funtzionatzea ahalbidetzen duen argumentu gehigarri bat erabiltzen dugu:
$ ./mstest8 --port 7010 --lport 9010 --gen 440
Momentu honetan, "urruneko" gailura bidalitako paketeak TShark-ekin kontsolan keinuka hasi behar dira eta ordenagailuaren bozgorailutik etengabeko tonu bat entzungo da.
Dena idatzitako moduan gertatu bada, programaren bigarren kopia berrabiaraziko dugu, baina "βgen 440" gako eta argumenturik gabe. Sorgailuaren papera izango duzu orain. Horren ondoren, zarata atera dezakezu mikrofonoan; dagokion soinua entzun beharko zenuke bozgorailuan edo entzungailuetan. Autoekzitazio akustikoa ere gerta daiteke; bozgorailuaren bolumena jaitsi eta efektua desagertu egingo da.
Bi ordenagailutan exekutatu baduzu eta IP helbideei buruz nahastu ez bazenuen, emaitza bera izango duzu zain: bi norabideko kalitate digitaleko ahots komunikazioa.
Hurrengo artikuluan gure iragazkiak - pluginak nola idazten ikasiko dugu, trebetasun honi esker multimedia-streamer-a audio eta bideorako ez ezik, beste arlo zehatz batzuetan ere erabili ahal izango duzu.
Iturria: www.habr.com