Raziskovanje mehanizma VoIP Mediastreamer2. 6. del

Gradivo članka je vzeto iz mojega zen kanal.

Prenos zvočnega signala preko RTP toka

Raziskovanje mehanizma VoIP Mediastreamer2. 6. del

V preteklosti članek Sestavili smo vezje za daljinsko upravljanje iz generatorja tonov in detektorja tonov, ki delujeta v okviru istega programa. V tem članku se bomo naučili uporabljati protokol RTP (RFC 3550 - RTP: transportni protokol za aplikacije v realnem času) za sprejemanje/oddajanje zvočnega signala preko omrežja Ethernet.

protokol RTP (Protokol v realnem času) prevedeno pomeni protokol v realnem času, uporablja se za prenos zvoka, videa, podatkov, vsega kar zahteva prenos v realnem času. Vzemimo za primer zvočni signal. Fleksibilnost protokola je tolikšna, da vam omogoča prenos zvočnega signala z vnaprej določeno kakovostjo.

Prenos se izvaja s pomočjo UDP paketov, kar pomeni, da je izguba paketov med prenosom povsem sprejemljiva. Vsak paket vsebuje posebno glavo RTP in podatkovni blok oddanega signala. Glava vsebuje naključno izbran identifikator vira signala, informacije o vrsti signala, ki se prenaša, in edinstveno zaporedno številko paketa, tako da so lahko paketi razporejeni v pravilnem vrstnem redu pri dekodiranju, ne glede na vrstni red, v katerem jih je dostavil omrežje. Glava lahko vsebuje tudi dodatne informacije, tako imenovano razširitev, ki omogoča prilagoditev glave za uporabo v določenem opravilu aplikacije.

Podatkovni blok vsebuje vsebino paketa. Notranja organizacija vsebine je odvisna od vrste obremenitve, lahko so to vzorci mono signala, stereo signala, vrstica video slike itd.

Vrsta obremenitve je označena s sedembitno številko. Priporočilo RFC3551 (Profil RTP za avdio in video konference z minimalnim nadzorom) določa več vrst obremenitev; ustrezna tabela vsebuje opis vrst obremenitev in pomen kod, s katerimi so označene. Nekatere kode niso strogo vezane na nobeno vrsto obremenitve, z njimi lahko označimo poljubno obremenitev.

Velikost podatkovnega bloka je zgoraj omejena z največjo velikostjo paketa, ki se lahko prenaša v danem omrežju brez segmentacije (parameter MTU). Na splošno to ni več kot 1500 bajtov. Če želite torej povečati količino prenesenih podatkov na sekundo, lahko povečate velikost paketa do določene točke, nato pa boste morali povečati pogostost pošiljanja paketov. V medijskem pretakalniku je to nastavljiva nastavitev. Privzeto je 50 Hz, tj. 50 paketov na sekundo. Zaporedje prenesenih RTP paketov bomo imenovali RTP tok.

Za začetek prenosa podatkov med virom in sprejemnikom je dovolj, da oddajnik pozna IP naslov sprejemnika in številko vrat, ki jih uporablja za sprejem. Tisti. brez kakršnih koli predhodnih postopkov začne vir oddajati podatke, prejemnik pa jih je pripravljen takoj sprejeti in obdelati. V skladu s standardom mora biti številka vrat, ki se uporabljajo za prenos ali sprejem toka RTP, soda.

V situacijah, ko naslova prejemnika ni mogoče vedeti vnaprej, se uporabljajo strežniki, kjer prejemniki pustijo svoj naslov, oddajnik pa ga lahko zahteva s sklicevanjem na neko edinstveno ime prejemnika.

V primerih, ko kakovost komunikacijskega kanala ali zmogljivosti sprejemnika ni znana, se organizira povratni kanal, preko katerega lahko sprejemnik obvesti oddajnik o svojih zmogljivostih, številu zgrešenih paketov ipd. Ta kanal uporablja protokol RTCP. Format paketov, ki se prenašajo po tem kanalu, je definiran v RFC 3605. Po tem kanalu se prenaša razmeroma malo podatkov, 200..300 bajtov na sekundo, tako da na splošno njegova prisotnost ni obremenjujoča. Številka vrat, kamor so poslani paketi RTCP, mora biti liha in za eno večja od številke vrat, iz katerih prihaja tok RTP. V našem primeru tega kanala ne bomo uporabili, saj zmogljivosti sprejemnika in kanala očitno presegajo naše, zaenkrat skromne potrebe.

V našem programu bo vezje za prenos podatkov, za razliko od prejšnjega primera, razdeljeno na dva dela: oddajno pot in sprejemno pot. Za vsak del bomo naredili svoj izvor ure, kot je prikazano na naslovni sliki.

Enosmerna komunikacija med njima bo potekala po protokolu RTP. V tem primeru ne potrebujemo zunanjega omrežja, saj bosta oddajnik in sprejemnik na istem računalniku – paketi bodo potovali znotraj njega.

Za vzpostavitev toka RTP medijski pretočnik uporablja dva filtra: MS_RTP_SEND in MS_RTP_RECV. Prvi oddaja drugi in sprejema tok RTP. Da bi ti filtri delovali, morajo posredovati kazalec na objekt seje RTP, ki lahko pretvori tok podatkovnih blokov v tok paketov RTP ali naredi nasprotno. Ker se notranji format podatkov medijskega pretakalnika ne ujema s formatom podatkov paketa RTP, morate pred prenosom podatkov v MS_RTP_SEND uporabiti filter kodirnika, ki pretvori 16-bitne vzorce zvočnega signala v osem-bitne, kodirane v skladu z u-zakon (mu-zakon). Na sprejemni strani ima dekodirni filter nasprotno funkcijo.

Spodaj je besedilo programa, ki izvaja shemo, prikazano na sliki (simboli # pred direktivami za vključitev so bili odstranjeni, ne pozabite jih vključiti):

/* Файл 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);
}
}

Sestavljamo in izvajamo. Program bo deloval kot v prejšnjem primeru, le da se bodo podatki prenašali preko toka RTP.

V naslednjem članku bomo ta program razdelili na dve neodvisni aplikaciji - sprejemnik in oddajnik ter ju zagnali v različnih terminalih. Hkrati se bomo naučili analizirati RTP pakete s programom TShark.

Vir: www.habr.com

Dodaj komentar