Entdeckt de Mediastreamer2 VoIP-Motor. Deel 6

D'Material vum Artikel ass aus mengem zen Kanal.

En Audio Signal iwwer RTP Stream iwwerdroen

Entdeckt de Mediastreamer2 VoIP-Motor. Deel 6

An der leschter Artikel Mir hunn e Fernbedienungsschaltung aus engem Tongenerator an engem Tondetektor zesummegesat, deen am selwechte Programm funktionnéiert. An dësem Artikel léiere mir wéi Dir de RTP Protokoll benotzt (RFC 3550 - RTP: A Transport Protokoll fir Echtzäit Uwendungen) fir en Audiosignal iwwer en Ethernet Netzwierk ze kréien/iwwerdroen.

RTP Protokoll (Echtzäit Protokoll) iwwersat heescht Echtzäitprotokoll, et gëtt benotzt fir Audio, Video, Daten ze vermëttelen, alles wat d'Iwwerdroung an Echtzäit erfuerdert. Loosst eis en Audiosignal als Beispill huelen. D'Flexibilitéit vum Protokoll ass sou datt et Iech erlaabt en Audiosignal mat enger virbestëmmter Qualitéit ze vermëttelen.

D'Transmissioun gëtt mat UDP Päck duerchgefouert, dat heescht datt de Paketverloscht zimmlech akzeptabel ass während der Iwwerdroung. All Paket enthält e speziellen RTP Header an en Dateblock vum iwwerdroene Signal. Den Header enthält e zoufälleg ausgewielten Signalquellidentifizéierer, Informatioun iwwer d'Zort vum Signal, deen iwwerdroe gëtt, an eng eenzegaarteg Paketsequenznummer, sou datt d'Päckchen an der korrekter Uerdnung arrangéiert kënne ginn beim Dekodéierung, onofhängeg vun der Uerdnung an där se vum Reseau. Den Header kann och zousätzlech Informatioun enthalen, déi sougenannt Extensioun, déi et erlaabt datt den Header fir d'Benotzung an enger spezifescher Applikatiounsaufgab ugepasst gëtt.

Den Dateblock enthält d'Notzlaascht vum Paket. Déi intern Organisatioun vum Inhalt hänkt vun der Aart vun der Laascht of, et kann Proben vun engem Mono-Signal, e Stereo-Signal, e Video-Bildlinn, etc.

D'Laaschttyp gëtt mat enger siwen-Bit Zuel uginn. Recommandatioun RFC3551 (RTP Profil fir Audio a Video Konferenzen mat minimaler Kontroll) etabléiert verschidden Zorte vu Laascht; déi entspriechend Tabell liwwert eng Beschreiwung vun den Aarte vu Laascht an d'Bedeitung vun de Coden, duerch déi se bezeechent ginn. E puer Coden sinn net strikt un all Zort vu Laascht gebonnen; Si kënne benotzt ginn fir eng arbiträr Laascht ze bezeechnen.

D'Gréisst vun engem Dateblock ass uewen limitéiert duerch déi maximal Paketgréisst déi op engem bestëmmten Netzwierk ouni Segmentatioun (MTU Parameter) iwwerdroe ka ginn. Am Allgemengen ass dëst net méi wéi 1500 Bytes. Also, fir d'Quantitéit un Daten pro Sekonn iwwerdroen ze erhéijen, kënnt Dir d'Pakgréisst bis zu engem gewësse Punkt erhéijen, an da musst Dir d'Frequenz vun der Sendung vun de Pakete erhéijen. An engem Media Streamer ass dëst eng konfiguréierbar Astellung. Par défaut ass et 50 Hz, d.h. 50 Pakete pro Sekonn. Mir nennen d'Sequenz vun iwwerdroene RTP-Päckchen en RTP-Stream.

Fir unzefänken Daten tëscht der Quell an dem Empfänger ze vermëttelen, ass et genuch fir de Sender d'IP Adress vum Empfänger an d'Portnummer ze kennen déi se benotzt fir ze kréien. Déi. ouni virleefeg Prozeduren fänkt d'Quell Daten ze vermëttelen, an den Empfänger, am Tour, ass prett fir se direkt ze kréien an ze veraarbecht. Geméiss dem Standard muss d'Portnummer, déi benotzt gëtt fir en RTP-Stream ze vermëttelen oder ze kréien, egal sinn.

A Situatiounen wou et onméiglech ass d'Adress vum Empfänger am Viraus ze kennen, ginn Server benotzt wou Empfänger hir Adress verloossen, an de Sender kann et ufroen andeems Dir op e puer eenzegaartegen Numm vum Empfänger referéiert.

A Fäll wou d'Qualitéit vum Kommunikatiounskanal oder d'Fäegkeeten vum Empfänger onbekannt sinn, gëtt e Feedback-Kanal organiséiert, duerch deen den Empfänger de Sender iwwer seng Fäegkeeten informéiere kann, d'Zuel vun de Päckchen, dee se verpasst huet, asw. Dëse Kanal benotzt den RTCP Protokoll. D'Format vun de Pakete, déi an dësem Kanal iwwerdroe ginn, gëtt am RFC 3605 definéiert. Relativ wéineg Daten ginn iwwer dëse Kanal iwwerdroen, 200..300 Bytes pro Sekonn, also am Allgemengen ass seng Präsenz net belaaschtend. D'Portnummer, op déi RTCP Päck geschéckt ginn, muss komesch sinn an eng méi grouss wéi d'Portnummer, aus där de RTP-Stream kënnt. An eisem Beispill wäerte mir dëse Kanal net benotzen, well d'Fäegkeeten vum Empfänger a Kanal selbstverständlech eis, bis elo bescheiden, Bedierfnesser iwwerschreiden.

An eisem Programm gëtt den Datenübertragungskrees, am Géigesaz zu dem viregten Beispill, an zwee Deeler opgedeelt: e Sendungswee an e Empfangswee. Fir all Deel wäerte mir eis eege Auer Quell maachen, wéi am Titel Bild gewisen.

One-Manéier Kommunikatioun tëscht hinnen gëtt mam RTP Protokoll duerchgefouert. An dësem Beispill brauche mir keen externen Netzwierk, well souwuel de Sender wéi och den Empfänger um selwechte Computer sinn - d'Päckchen reesen dobannen.

Fir e RTP-Stream opzebauen, benotzt de Medienstreamer zwee Filtere: MS_RTP_SEND an MS_RTP_RECV. Deen éischten iwwerdréit deen zweeten a kritt den RTP Stream. Fir datt dës Filtere funktionnéieren, musse se e Pointer op en RTP-Sessiounsobjekt passéieren, deen entweder e Stroum vun Dateblocken an e Stroum vun RTP-Päckchen konvertéiere kann oder de Géigendeel maachen. Well den internen Dateformat vum Medienstreamer net mam Dateformat vum RTP-Paket entsprécht, ier Dir d'Donnéeën op MS_RTP_SEND transferéiert, musst Dir en Encoderfilter benotzen deen 16-Bit Audio Signal Echantillon an aacht-Bit encodéiert konvertéiert. u-Gesetz (mu-Gesetz). Op der Empfangssäit mécht den Decoderfilter déi entgéintgesate Funktioun.

Drënner ass den Text vum Programm deen d'Schema implementéiert, déi an der Figur ugewise gëtt (d'# Symboler ier d'Inklusiounsdirektive geläscht goufen, vergiesst se net ze enthalen):

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

Mir kompiléieren a lafen. De Programm funktionnéiert wéi am virege Beispill, awer d'Donnéeë ginn iwwer en RTP Stream iwwerdroen.

Am nächsten Artikel wäerte mir dëse Programm an zwou onofhängeg Applikatiounen opdeelen - en Empfänger an e Sender a starten se a verschiddene Terminaler. Zur selwechter Zäit léiere mir wéi Dir RTP-Pakete mat dem TShark Programm analyséiert.

Source: will.com

Setzt e Commentaire