Materialet i artiklen er taget fra min .
Overførsel af lydsignal via RTP-strøm

I den forrige Vi har samlet et diagram til fjernbetjening fra generatoren og detektoren for tonale signaler, som fungerer inden for et enkelt program. I denne artikel vil vi lære at bruge RTP-protokollen (RFC 3550 - RTP: En transportprotokol til realtidsapplikationer) til modtagelse/overførsel af lydsignal over Ethernet-netværk.
RTP-protokollen (Real Time Protocol) betyder bogstaveligt talt realtidsprotokol, og den bruges til at transmittere lyd, video, data, alt der kræver realtidskommunikation. Som eksempel kan vi tage et lydsignal. Protokollens fleksibilitet tillader overførsel af lydsignalet med forudbestemt kvalitet.
Overførselen udføres ved hjælp af UDP-pakker, hvilket betyder, at der kan forekomme tab af pakker under overførslen. Hver pakke indeholder en særlig RTP-header og et datablok for det transmitterede signal. I headeren findes en tilfældigt valgt kildeidentifikator, information om typen af det transmitterede signal, et unikt sekvensnummer for pakken, som gør det muligt at genopbygge pakkerne i den korrekte rækkefølge ved dekodning, uanset i hvilken rækkefølge de blev leveret af netværket. Headeren kan også indeholde yderligere information, kaldet en udvidelse, der gør det muligt at tilpasse headeren til anvendelse i en bestemt applikation.
Databloceret indeholder nyttelasten for pakken. Den interne organisering af indholdet afhænger af typen af nyttelast, det kan være målinger af monofonisk signal, stereosignal, videobillede linje osv.
Typen af nyttelast angives med et syv-bit tal. Anbefaling RFC3551 (RTP Profile for Audio and Video Conferences with Minimal Control) fastsætter flere typer nyttelast i den relevante tabel, hvor beskrivelsen af typerne og værdien af koderne, der betegner dem, er angivet. Nogle koder er ikke strengt knyttet til en bestemt type nyttelast - de kan bruges til at betegne vilkårlig nyttelast.
Størrelsen af datablokken er begrænset af den maksimale pakke størrelse, der kan overføres i dette netværk uden segmentering (MTU-parameteren). Generelt er dette ikke mere end 1500 byte. For at øge mængden af data, der overføres pr. sekund, kan pakke størrelsen øges op til et vist punkt, hvorefter frekvensen for pakkeoverførsel skal øges. For en mediestrømmer er dette en justerbar parameter. Som standard er den 50 Hz, dvs. 50 pakker pr. sekund. Sekvensen af de overførte RTP-pakker kaldes en RTP-strøm.
For at starte dataoverførslen mellem kilde og modtager, skal afsenderen blot vide IP-adressen på modtageren og det portnummer, som modtageren bruger til at modtage. Det vil sige, uden nogen forberedende procedurer, begynder kilden at sende data, og modtageren er klar til straks at modtage og behandle dem. Ifølge standarden skal portnummeret, der bruges til overførsel eller modtagelse af RTP-strømmen, være lige.
I situationer hvor man ikke på forhånd kan kende adressen på modtageren, bruges servere, hvor modtagerne efterlader deres adresse, og afsenderen kan anmode om det med henvisning til et unikt navn for modtageren.
I tilfælde, hvor kvaliteten af kommunikationskanalen eller modtagerens kapaciteter er ukendte, organiseres der en feedbackkanal, så modtageren kan informere afsenderen om sine evner, antallet af pakker, som han mangler, osv. I denne kanal anvendes RTCP-protokollen. Formatet af de pakker, der sendes i denne kanal, er defineret i RFC 3605. Gennem denne kanal overføres der relativt lidt data, 200-300 byte pr. sekund, så dens tilstedeværelse er ikke en byrde. Portnummeret, hvortil RTCP-pakker sendes, skal være ulige og én større end portnummeret, som RTP-strømmen kommer fra. I vores eksempel vil vi ikke bruge denne kanal, da modtagerens og kanalens kapaciteter på forhånd overstiger vores, hidtil beskedne, behov.
I vores program vil datatransmissionsskemaet, i modsætning til skemaet fra det tidligere eksempel, være opdelt i to dele: transmissionsvejen og modtagervejen. For hver del vil vi lave vores egen urkilde, som vist på det øverste billede.
Der ensrettede kommunikation mellem dem vil blive udført ved hjælp af RTP-protokollen. I dette eksempel behøver vi ikke et ekstern netværk, da både senderen og modtageren vil være placeret på den samme computer — pakkerne vil blive sendt indeni den.
Til at etablere RTP-strømmen i mediestrømmeren bruges der to filtre: MS_RTP_SEND og MS_RTP_RECV. Det første sender den anden modtager RTP-strømmen. For at disse filtre kan begynde at fungere, skal de modtage en pegepind til RTP-sessionen, som kan udføre både konvertering af data blokstrøm til RTP-pakker og den omvendte handling. Da det interne dataformat i mediestrømmeren ikke stemmer overens med dataformatet for RTP-pakken, skal der bruges et konverteringsfilter (encoder) før overførsel af data til MS_RTP_SEND, som konverterer 16-bit af lydsignaler til otte-bit, kodet efter u-loven (my-loven). På modtagerens side udfører decoder-filteret den omvendte funktion.
Nedenfor er teksten til programmet, der implementerer skemaet vist på billedet (symbolet # foran include-direktiverne er fjernet, husk at tilføje dem):
/* Файл 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 kompilere og kører. Programmet vil fungere som i det foregående eksempel, men dataene vil blive sendt via RTP-strømmen.
I den næste artikel vil vi opdele dette program i to uafhængige applikationer - modtager og sender og køre dem i forskellige terminaler. Samtidig vil vi lære at analysere RTP-pakker ved hjælp af programmet TShark.
Kilde: habr.com
