Utforsker Mediastreamer2 VoIP-motoren. Del 6

Stoffet til artikkelen er hentet fra min zen-kanal.

Sender et lydsignal via RTP-strøm

Utforsker Mediastreamer2 VoIP-motoren. Del 6

I den siste artikkel Vi har satt sammen en fjernkontrollkrets fra en tonegenerator og en tonedetektor som opererer innenfor samme program. I denne artikkelen vil vi lære hvordan du bruker RTP-protokollen (RFC 3550 - RTP: En transportprotokoll for sanntidsapplikasjoner) for å motta/sende et lydsignal over et Ethernet-nettverk.

RTP-protokoll (Sanntidsprotokoll) oversatt betyr sanntidsprotokoll, den brukes til å overføre lyd, video, data, alt som krever overføring i sanntid. La oss ta et lydsignal som et eksempel. Fleksibiliteten til protokollen er slik at den lar deg overføre et lydsignal med en forhåndsbestemt kvalitet.

Overføringen utføres ved hjelp av UDP-pakker, noe som betyr at pakketap er ganske akseptabelt under overføring. Hver pakke inneholder en spesiell RTP-header og en datablokk for det overførte signalet. Headeren inneholder en tilfeldig valgt signalkildeidentifikator, informasjon om typen signal som sendes, og et unikt pakkesekvensnummer slik at pakkene kan ordnes i riktig rekkefølge ved dekoding, uavhengig av rekkefølgen de ble levert i av Nettverk. Headeren kan også inneholde tilleggsinformasjon, den såkalte utvidelsen, som gjør at headeren kan tilpasses for bruk i en spesifikk applikasjonsoppgave.

Datablokken inneholder pakkens nyttelast. Den interne organiseringen av innholdet avhenger av typen belastning, det kan være prøver av et monosignal, et stereosignal, en videobildelinje, etc.

Lasttypen er angitt med et syv-bits tall. Anbefaling RFC3551 (RTP-profil for lyd- og videokonferanser med minimal kontroll) etablerer flere typer last; den tilsvarende tabellen gir en beskrivelse av lasttypene og betydningen av kodene de er angitt med. Noen koder er ikke strengt knyttet til noen type belastning; de kan brukes til å angi en vilkårlig belastning.

Størrelsen på en datablokk er begrenset ovenfor av den maksimale pakkestørrelsen som kan overføres på et gitt nettverk uten segmentering (MTU-parameter). Generelt er dette ikke mer enn 1500 byte. For å øke mengden data som sendes per sekund, kan du øke pakkestørrelsen opp til et visst punkt, og deretter må du øke frekvensen for å sende pakker. I en mediastreamer er dette en konfigurerbar innstilling. Som standard er det 50 Hz, dvs. 50 pakker per sekund. Vi vil kalle sekvensen av overførte RTP-pakker for en RTP-strøm.

For å begynne å overføre data mellom kilden og mottakeren er det nok at senderen kjenner IP-adressen til mottakeren og portnummeret den bruker for mottak. De. uten noen foreløpige prosedyrer begynner kilden å overføre data, og mottakeren er på sin side klar til å umiddelbart motta og behandle dem. I henhold til standarden må portnummeret som brukes til å sende eller motta en RTP-strøm være jevnt.

I situasjoner der det er umulig å vite mottakerens adresse på forhånd, brukes servere der mottakerne legger igjen adressen sin, og senderen kan be om det ved å referere til et unikt navn på mottakeren.

I tilfeller hvor kvaliteten på kommunikasjonskanalen eller evnene til mottakeren er ukjent, organiseres en tilbakemeldingskanal der mottakeren kan informere senderen om dens evner, antall pakker den har gått glipp av osv. Denne kanalen bruker RTCP-protokollen. Formatet på pakker som sendes i denne kanalen er definert i RFC 3605. Relativt lite data overføres over denne kanalen, 200..300 byte per sekund, så generelt er tilstedeværelsen ikke tyngende. Portnummeret som RTCP-pakker sendes til må være oddetall og én større enn portnummeret som RTP-strømmen kommer fra. I vårt eksempel vil vi ikke bruke denne kanalen, siden egenskapene til mottakeren og kanalen åpenbart overgår våre, så langt beskjedne, behov.

I vårt program vil dataoverføringskretsen, i motsetning til det forrige eksemplet, deles inn i to deler: en sendebane og en mottaksbane. For hver del vil vi lage vår egen klokkekilde, som vist på tittelbildet.

Enveiskommunikasjon mellom dem vil bli utført ved hjelp av RTP-protokollen. I dette eksemplet trenger vi ikke et eksternt nettverk, siden både senderen og mottakeren vil være plassert på samme datamaskin - pakkene vil reise inne i den.

For å etablere en RTP-strøm bruker mediastreameren to filtre: MS_RTP_SEND og MS_RTP_RECV. Den første sender den andre og mottar RTP-strømmen. For at disse filtrene skal fungere, må de sende en peker til et RTP-øktobjekt, som enten kan konvertere en strøm av datablokker til en strøm av RTP-pakker eller gjøre det motsatte. Siden det interne dataformatet til mediastreameren ikke samsvarer med dataformatet til RTP-pakken, før du overfører dataene til MS_RTP_SEND, må du bruke et kodefilter som konverterer 16-bits lydsignaleksempler til åtte-bits kodet i henhold til u-lov (mu-lov). På mottakersiden utfører dekoderfilteret motsatt funksjon.

Nedenfor er teksten til programmet som implementerer skjemaet vist i figuren (#-symbolene før inkluderingsdirektivene er fjernet, ikke glem å inkludere 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 kompilerer og kjører. Programmet vil fungere som i forrige eksempel, men dataene vil bli overført via en RTP-strøm.

I den neste artikkelen vil vi dele dette programmet inn i to uavhengige applikasjoner - en mottaker og en sender og starte dem i forskjellige terminaler. Samtidig vil vi lære hvordan du analyserer RTP-pakker ved hjelp av TShark-programmet.

Kilde: www.habr.com

Legg til en kommentar