Að kanna Mediastreamer2 VoIP vélina. 6. hluti

Efni greinarinnar er tekið úr mínum zen rás.

Sendir hljóðmerki í gegnum RTP straum

Að kanna Mediastreamer2 VoIP vélina. 6. hluti

Í fortíðinni grein Við höfum sett saman fjarstýringarrás úr tóngjafa og tónskynjara sem starfa innan sama forrits. Í þessari grein munum við læra hvernig á að nota RTP samskiptareglur (RFC 3550 - RTP: Flutningabókun fyrir rauntímaforrit) til að taka á móti/senda hljóðmerki um Ethernet net.

RTP samskiptareglur (Rauntímabókun) þýtt þýðir rauntíma siðareglur, það er notað til að senda hljóð, myndbönd, gögn, allt sem krefst sendingar í rauntíma. Við skulum taka hljóðmerki sem dæmi. Sveigjanleiki samskiptareglunnar er slíkur að hún gerir þér kleift að senda hljóðmerki með fyrirfram ákveðnum gæðum.

Sendingin fer fram með UDP-pökkum, sem þýðir að pakkatap er alveg ásættanlegt við sendingu. Hver pakki inniheldur sérstakan RTP haus og gagnablokk af sendu merkinu. Hausinn inniheldur af handahófi valið auðkenni merkjagjafa, upplýsingar um tegund merkis sem verið er að senda og einstakt raðnúmer pakka þannig að hægt sé að raða pökkunum í rétta röð við afkóðun, óháð því í hvaða röð þeir voru afhentir af net. Hausinn getur einnig innihaldið viðbótarupplýsingar, svokallaða viðbót, sem gerir kleift að aðlaga hausinn til notkunar í tilteknu forritsverkefni.

Gagnablokkin inniheldur burðargetu pakkans. Innra skipulag efnisins fer eftir tegund hleðslu, það getur verið sýnishorn af mónómerki, steríómerki, myndbandsmyndlínu osfrv.

Hleðslugerðin er auðkennd með sjö bita tölu. Tilmæli RFC3551 (RTP prófíl fyrir hljóð- og myndráðstefnur með lágmarksstýringu) setur upp nokkrar tegundir álags; samsvarandi tafla gefur lýsingu á tegundum álags og merkingu kóðanna sem þeir eru tilgreindir með. Sumir kóðar eru ekki stranglega bundnir við hvers kyns álag; þeir geta verið notaðir til að tilgreina handahófskennt álag.

Stærð gagnablokkar er takmörkuð hér að ofan af hámarks pakkastærð sem hægt er að senda á tilteknu neti án skiptingar (MTU breytu). Almennt séð er þetta ekki meira en 1500 bæti. Þannig að til að auka gagnamagnið sem er sent á sekúndu geturðu aukið pakkastærðina upp að ákveðnum punkti og þá þarftu að auka tíðni pakkasendinga. Í fjölmiðlastraumspilara er þetta stillanleg stilling. Sjálfgefið er 50 Hz, þ.e. 50 pakkar á sekúndu. Við munum kalla röð sendra RTP pakka RTP straum.

Til að byrja að senda gögn á milli uppsprettu og móttakara er nóg að sendirinn viti IP tölu móttakarans og gáttarnúmerið sem hann notar til móttöku. Þeir. án nokkurra bráðabirgðaaðgerða byrjar uppspretta að senda gögn og móttakandinn er aftur á móti tilbúinn til að taka á móti og vinna úr þeim strax. Samkvæmt staðlinum verður gáttarnúmerið sem notað er til að senda eða taka á móti RTP straumi að vera jafnt.

Í aðstæðum þar sem ómögulegt er að vita heimilisfang móttakandans fyrirfram eru netþjónar notaðir þar sem viðtakendur skilja eftir heimilisfang sitt og sendirinn getur beðið um það með því að vísa í eitthvert einstakt nafn viðtakandans.

Í þeim tilfellum þar sem gæði samskiptarásarinnar eða hæfileikar móttakandans eru óþekktir er skipulögð endurgjöfarrás þar sem viðtakandinn getur upplýst sendandann um getu sína, fjölda pakka sem hann missti af o.s.frv. Þessi rás notar RTCP samskiptareglur. Snið pakka sem eru sendar á þessari rás er skilgreint í RFC 3605. Tiltölulega lítil gögn eru send yfir þessa rás, 200..300 bæti á sekúndu, þannig að almennt er tilvist þeirra ekki íþyngjandi. Gáttarnúmerið sem RTCP pakkar eru sendir til verður að vera odda og einu stærra en gáttarnúmerið sem RTP straumurinn kemur frá. Í okkar dæmi munum við ekki nota þessa rás, þar sem hæfileikar móttakarans og rásarinnar eru augljóslega umfram, hingað til hóflegar, þarfir okkar.

Í forritinu okkar verður gagnaflutningsrásinni, ólíkt fyrra dæmi, skipt í tvo hluta: sendingarleið og móttökuleið. Fyrir hvern hluta munum við búa til okkar eigin klukkuuppsprettu, eins og sýnt er á titilmyndinni.

Einstefnusamskipti á milli þeirra munu fara fram með því að nota RTP-samskiptareglur. Í þessu dæmi þurfum við ekki utanaðkomandi net, þar sem bæði sendirinn og móttakandinn verða staðsettir á sömu tölvunni - pakkarnir ferðast inni í henni.

Til að koma á RTP straumi notar fjölmiðlastraumurinn tvær síur: MS_RTP_SEND og MS_RTP_RECV. Sá fyrsti sendir þann seinni og tekur á móti RTP straumnum. Til þess að þessar síur virki þurfa þær að senda bendi í RTP lotuhlut, sem getur annað hvort breytt straumi gagnablokka í straum af RTP pakka eða gert hið gagnstæða. Þar sem innra gagnasnið miðlunarstraumsins passar ekki við gagnasnið RTP pakkans, áður en gögnin eru flutt yfir á MS_RTP_SEND, þarftu að nota kóðara síu sem breytir 16 bita hljóðmerkjasýnum í átta bita kóðuð skv. u-lög (mú-lög). Á móttökuhliðinni sinnir afkóðarasían hið gagnstæða hlutverk.

Hér að neðan er texti forritsins sem útfærir kerfið sem sýnt er á myndinni (# táknin á undan innihaldsleiðbeiningunum hafa verið fjarlægð, ekki gleyma að hafa þau með):

/* Файл mstest6.c Имитатор пульта управления и приемника. */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msvolume.h>
#include <mediastreamer2/mstonedetector.h>
#include <mediastreamer2/msrtp.h>
#include <ortp/rtpsession.h>
#include <ortp/payloadtype.h>
/* Подключаем заголовочный файл с функциями управления событиями
* медиастримера.*/
include <mediastreamer2/mseventqueue.h>
#define PCMU 0
/* Функция обратного вызова, она будет вызвана фильтром, как только он
обнаружит совпадение характеристик входного сигнала с заданными. */
static void tone_detected_cb(void *data, MSFilter *f, unsigned int event_id,
MSToneDetectorEvent *ev)
{
printf("Принята команда: %sn", ev->tone_name);
}
/*----------------------------------------------------------------------------*/
/* Функция регистрации типов полезных нагрузок. */
void register_payloads(void)
{
/*Регистрируем типы нагрузок в таблице профилей. Позднее, по индексу
взятому из заголовка RTP-пакета из этой таблицы будут извлекаться
параметры нагрузки, необходимые для декодирования данных пакета. */
rtp_profile_set_payload (&av_profile, PCMU, &payload_type_pcm8000);
}
/*----------------------------------------------------------------------------*/
/* Эта функция создана из функции create_duplex_rtpsession() в audiostream.c
медиастримера2. */
static RtpSession *
create_rtpsession (int loc_rtp_port, int loc_rtcp_port,
bool_t ipv6, RtpSessionMode mode)
{
RtpSession *rtpr;
rtpr = rtp_session_new ((int) mode);
rtp_session_set_scheduling_mode (rtpr, 0);
rtp_session_set_blocking_mode (rtpr, 0);
rtp_session_enable_adaptive_jitter_compensation (rtpr, TRUE);
rtp_session_set_symmetric_rtp (rtpr, TRUE);
rtp_session_set_local_addr (rtpr, ipv6 ? "::" : "0.0.0.0", loc_rtp_port,
loc_rtcp_port);
rtp_session_signal_connect (rtpr, "timestamp_jump",
(RtpCallback) rtp_session_resync, 0);
rtp_session_signal_connect (rtpr, "ssrc_changed",
(RtpCallback) rtp_session_resync, 0);
rtp_session_set_ssrc_changed_threshold (rtpr, 0);
rtp_session_set_send_payload_type(rtpr, PCMU);
/* По умолчанию выключаем RTCP-сессию, так как наш пульт не будет использовать её. */
rtp_session_enable_rtcp (rtpr, FALSE);
return rtpr;
}
/*----------------------------------------------------------------------------*/
int main()
{
ms_init();
/* Создаем экземпляры фильтров. */
MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
MSFilter *volume = ms_filter_new(MS_VOLUME_ID);
MSSndCard *card_playback =
ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback);
MSFilter *detector = ms_filter_new(MS_TONE_DETECTOR_ID);
/* Очищаем массив находящийся внутри детектора тонов, он описывает
* особые приметы разыскиваемых сигналов.*/
ms_filter_call_method(detector, MS_TONE_DETECTOR_CLEAR_SCANS, 0);
/* Подключаем к фильтру функцию обратного вызова. */
ms_filter_set_notify_callback(detector,
(MSFilterNotifyFunc)tone_detected_cb, NULL);
/* Создаем массив, каждый элемент которого описывает характеристику
* одного из тонов, который требуется обнаруживать: Текстовое имя
* данного элемента, частота в герцах, длительность в миллисекундах,
* минимальный уровень относительно 0,775В. */
MSToneDetectorDef scan[6]=
{
{"V+",440, 100, 0.1}, /* Команда "Увеличить громкость". */
{"V-",540, 100, 0.1}, /* Команда "Уменьшить громкость". */
{"C+",640, 100, 0.1}, /* Команда "Увеличить номер канала". */
{"C-",740, 100, 0.1}, /* Команда "Уменьшить номер канала". */
{"ON",840, 100, 0.1}, /* Команда "Включить телевизор". */
{"OFF", 940, 100, 0.1}/* Команда "Выключить телевизор". */
};
/* Передаем "приметы" сигналов детектор тонов. */
int i;
for (i = 0; i < 6; i++)
{
ms_filter_call_method(detector, MS_TONE_DETECTOR_ADD_SCAN,
&scan[i]);
}
/* Создаем фильтры кодера и декодера */
MSFilter *encoder = ms_filter_create_encoder("PCMU");
MSFilter *decoder=ms_filter_create_decoder("PCMU");
/* Регистрируем типы нагрузки. */
register_payloads();
/* Создаем RTP-сессию передатчика. */
RtpSession *tx_rtp_session = create_rtpsession (8010, 8011, FALSE, RTP_SESSION_SENDONLY);
rtp_session_set_remote_addr_and_port(tx_rtp_session,"127.0.0.1", 7010, 7011);
rtp_session_set_send_payload_type(tx_rtp_session, PCMU);
MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID);
ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, tx_rtp_session);
/* Создаем RTP-сессию приемника. */
MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
RtpSession *rx_rtp_session = create_rtpsession (7010, 7011, FALSE, RTP_SESSION_RECVONLY);
ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rx_rtp_session);
/* Создаем источники тактов - тикеры. */
MSTicker *ticker_tx = ms_ticker_new();
MSTicker *ticker_rx = ms_ticker_new();
/* Соединяем фильтры передатчика. */
ms_filter_link(voidsource, 0, dtmfgen, 0);
ms_filter_link(dtmfgen, 0, volume, 0);
ms_filter_link(volume, 0, encoder, 0);
ms_filter_link(encoder, 0, rtpsend, 0);
/* Соединяем фильтры приёмника. */
ms_filter_link(rtprecv, 0, decoder, 0);
ms_filter_link(decoder, 0, detector, 0);
ms_filter_link(detector, 0, snd_card_write, 0);
/* Подключаем источник тактов. */
ms_ticker_attach(ticker_tx, voidsource);
ms_ticker_attach(ticker_rx, rtprecv);
/* Настраиваем структуру, управляющую выходным сигналом генератора. */
MSDtmfGenCustomTone dtmf_cfg;
dtmf_cfg.tone_name[0] = 0;
dtmf_cfg.duration = 1000;
dtmf_cfg.frequencies[0] = 440;
/* Будем генерировать один тон, частоту второго тона установим в 0. */
dtmf_cfg.frequencies[1] = 0;
dtmf_cfg.amplitude = 1.0;
dtmf_cfg.interval = 0.;
dtmf_cfg.repeat_count = 0.;
/* Организуем цикл сканирования нажатых клавиш. Ввод нуля завершает
* цикл и работу программы. */
char key='9';
printf("Нажмите клавишу команды, затем ввод.n"
"Для завершения программы введите 0.n");
while(key != '0')
{
key = getchar();
if ((key >= 49) && (key <= 54))
{
printf("Отправлена команда: %cn", key);
/* Устанавливаем частоту генератора в соответствии с
* кодом нажатой клавиши. */
dtmf_cfg.frequencies[0] = 440 + 100*(key-49);
/* Включаем звуковой генератор c обновленной частотой. */
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
(void*)&dtmf_cfg);
}
/* Укладываем тред в спячку на 20мс, чтобы другие треды
* приложения получили время на работу. */
ms_usleep(20000);
}
}

Við tökum saman og keyrum. Forritið mun virka eins og í fyrra dæmi, en gögnin verða send í gegnum RTP straum.

Í næstu grein munum við skipta þessu forriti í tvö sjálfstæð forrit - móttakara og sendi og ræsa þau í mismunandi skautanna. Á sama tíma munum við læra hvernig á að greina RTP pakka með því að nota TShark forritið.

Heimild: www.habr.com

Bæta við athugasemd