Ferkenne de Mediastreamer2 VoIP-motor. Diel 6

It materiaal fan it artikel is nommen út myn zen kanaal.

It útstjoeren fan in audiosinjaal fia RTP-stream

Ferkenne de Mediastreamer2 VoIP-motor. Diel 6

Yn de lêste artikel Wy hawwe gearstald in ôfstân kontrôle circuit út in toan generator en in toan detector dy't operearje binnen itselde programma. Yn dit artikel sille wy leare hoe't jo it RTP-protokol brûke (RFC 3550 - RTP: In ferfiersprotokol foar realtime applikaasjes) foar it ûntfangen/ferstjoeren fan in audiosinjaal oer in Ethernet-netwurk.

RTP protokol (Real Time Protokol) oerset betsjut real-time protokol, it wurdt brûkt om audio, fideo, gegevens, alles wat oerdracht yn echte tiid fereasket. Lit ús nimme in audio sinjaal as foarbyld. De fleksibiliteit fan it protokol is sa dat it jo in audiosinjaal mei in foarbepaalde kwaliteit kin ferstjoere.

De oerdracht wurdt útfierd mei UDP-pakketten, wat betsjut dat pakketferlies frij akseptabel is tidens oerdracht. Elk pakket befettet in spesjale RTP-header en in gegevensblok fan it útstjoerde sinjaal. De koptekst befettet in willekeurich selekteare sinjaalboarne-identifikaasje, ynformaasje oer it type sinjaal dat wurdt ferstjoerd, en in unyk pakketfolchoardernûmer, sadat de pakketten yn 'e juste folchoarder kinne wurde regele by it dekodearjen, nettsjinsteande de folchoarder wêryn't se waarden levere troch de netwurk. De koptekst kin ek ekstra ynformaasje befetsje, de saneamde útwreiding, wêrtroch de koptekst oanpast wurde kin foar gebrûk yn in spesifike applikaasjetaak.

It gegevensblok befettet de lading fan it pakket. De ynterne organisaasje fan 'e ynhâld hinget ôf fan it type lading, it kin samples wêze fan in monosinjaal, in stereosinjaal, in fideoôfbyldingline, ensfh.

De lading type wurdt oanjûn troch in sân-bit nûmer. Oanbefelling RFC3551 (RTP-profyl foar audio- en fideokonferinsjes mei minimale kontrôle) stelt ferskate soarten lading fêst; de oerienkommende tabel jout in beskriuwing fan 'e soarten lading en de betsjutting fan 'e koades wêrmei't se binne oanwiisd. Guon koades binne net strikt bûn oan elk type lading; se kinne brûkt wurde om in willekeurige lading oan te jaan.

De grutte fan in gegevensblok wurdt hjirboppe beheind troch de maksimale pakketgrutte dy't kin wurde oerdroegen op in opjûne netwurk sûnder segmintaasje (MTU-parameter). Yn it algemien, dit is net mear as 1500 bytes. Sa kinne jo, om de hoemannichte gegevens per sekonde te ferheegjen, de pakketgrutte ferheegje oant in bepaald punt, en dan moatte jo de frekwinsje fan it ferstjoeren fan pakketten ferheegje. Yn in mediastreamer is dit in ynstelbere ynstelling. Standert is it 50 Hz, d.w.s. 50 pakketten per sekonde. Wy sille de folchoarder fan oerdroegen RTP-pakketten in RTP-stream neame.

Om te begjinnen mei it ferstjoeren fan gegevens tusken de boarne en de ûntfanger, is it genôch dat de stjoerder it IP-adres fan 'e ûntfanger wit en it poartenûmer dat it brûkt foar it ûntfangen. Dy. sûnder foarriedige prosedueres, de boarne begjint te ferstjoeren gegevens, en de ûntfanger, op syn beurt, is ree om fuortendaliks ûntfange en ferwurkjen. Neffens de standert moat it poartenûmer dat wurdt brûkt om in RTP-stream te ferstjoeren of te ûntfangen even wêze.

Yn situaasjes dêr't it ûnmooglik is om te witten it adres fan de ûntfanger fan tefoaren, tsjinners wurde brûkt dêr't ûntfangers ferlitte harren adres, en de stjoerder kin freegje it troch te ferwizen nei in unike namme fan de ûntfanger.

Yn gefallen dêr't de kwaliteit fan it kommunikaasjekanaal of de mooglikheden fan 'e ûntfanger ûnbekend binne, wurdt in feedbackkanaal organisearre wêrmei't de ûntfanger de stjoerder kin ynformearje oer syn mooglikheden, it oantal pakketten dat er mist hat, ensfh. Dit kanaal brûkt it RTCP-protokol. It formaat fan pakketten oerdroegen yn dit kanaal is definiearre yn RFC 3605. Relatyf lytse gegevens wurde oer dit kanaal oerbrocht, 200..300 bytes per sekonde, dus yn 't algemien is har oanwêzigens net lestich. It poartenûmer dêr't RTCP-pakketten nei stjoerd wurde moat ûneven wêze en ien grutter wêze dan it poartenûmer wêrfan de RTP-stream komt. Yn ús foarbyld sille wy dit kanaal net brûke, om't de mooglikheden fan 'e ûntfanger en kanaal fansels ús, oant no ta beskieden, ferlet binne.

Yn ús programma sil it gegevensferfierkring, yn tsjinstelling ta it foarige foarbyld, wurde ferdield yn twa dielen: in útstjoerpaad en in ûntfangerpaad. Foar elk diel sille wy ús eigen klokboarne meitsje, lykas werjûn yn 'e titelôfbylding.

Ien-wei kommunikaasje tusken har sil wurde útfierd mei it RTP-protokol. Yn dit foarbyld hawwe wy gjin ekstern netwurk nedich, om't sawol de stjoerder as de ûntfanger op deselde kompjûter sitte - de pakketten sille deryn reizgje.

Om in RTP-stream te meitsjen, brûkt de mediastreamer twa filters: MS_RTP_SEND en MS_RTP_RECV. De earste stjoert de twadde en ûntfangt de RTP-stream. Om dizze filters te wurkjen, moatte se in oanwizer trochjaan oan in RTP-sesjeobjekt, dat in stream fan gegevensblokken kin omsette yn in stream fan RTP-pakketten of it tsjinoerstelde dwaan kin. Om't it ynterne gegevensformaat fan 'e mediastreamer net oerienkomt mei it gegevensformaat fan it RTP-pakket, moatte jo foardat jo de gegevens oerdrage nei MS_RTP_SEND in encoderfilter brûke dat 16-bit audiosinjaalmonsters konvertearret yn acht-bit kodearre neffens de u-wet (mu-wet). Oan 'e ûntfangende kant docht it dekoderfilter de tsjinoerstelde funksje.

Hjirûnder is de tekst fan it programma dat it skema yn 'e figuer ymplementearret (de # symboalen foardat de omfetsje-rjochtlinen binne fuortsmiten, ferjit se net op te nimmen):

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

Wy kompilearje en rinne. It programma sil wurkje lykas yn it foarige foarbyld, mar de gegevens wurde oerdroegen fia in RTP-stream.

Yn it folgjende artikel sille wy dit programma ferdiele yn twa ûnôfhinklike applikaasjes - in ûntfanger en in stjoerder en lansearje se yn ferskate terminals. Tagelyk sille wy leare hoe't jo RTP-pakketten analysearje mei it TShark-programma.

Boarne: www.habr.com

Add a comment