Esplora u mutore VoIP Mediastreamer2. Parte 6

U materiale di l'articulu hè pigliatu da u mo canale zen.

Trasmette un signalu audio via flussu RTP

Esplora u mutore VoIP Mediastreamer2. Parte 6

In l'ultimu articulu Avemu assemblatu un circuitu di cuntrollu remoto da un generatore di tonu è un detector di tonu chì operanu in u stessu prugramma. In questu articulu avemu da amparà à utilizà u protocolu RTP (RFC 3550 - RTP: Un protocolu di trasportu per l'applicazioni in tempu reale) per riceve / trasmette un signalu audio nantu à una reta Ethernet.

protocolu RTP (Protocolu Real Time) traduttu significa protokollu in tempu reale, hè utilizatu per trasmette audio, video, dati, tuttu ciò chì necessita di trasmissione in tempu reale. Pigliemu un signalu audio per esempiu. A flessibilità di u protocolu hè tale chì permette di trasmette un signalu audio cù una qualità predeterminata.

A trasmissione hè realizata cù pacchetti UDP, chì significa chì a perdita di pacchetti hè abbastanza accettabile durante a trasmissione. Ogni pacchettu cuntene un header RTP speciale è un bloccu di dati di u signale trasmessu. L'intestazione cuntene un identificatore di fonte di signale selezziunatu aleatoriamente, infurmazione nantu à u tipu di signale chì hè trasmessu, è un numeru di sequenza di pacchettu unicu in modu chì i pacchetti ponu esse disposti in l'ordine currettu quandu si decodificanu, indipendentemente da l'ordine in quale sò stati spediti da u pacchettu. rete. L'intestazione pò ancu cuntene infurmazioni supplementari, a chjamata estensione, chì permette à l'intestazione per esse adattatu per l'usu in un compitu specificu di l'applicazione.

U bloccu di dati cuntene a carica di u pacchettu. L'urganizazione interna di u cuntenutu dipende di u tipu di carica, pò esse campioni di un signalu mono, un signalu stereo, una linea d'imaghjini video, etc.

U tipu di carica hè indicatu da un numeru di sette bit. Raccomandazione RFC3551 (Profilu RTP per Conferenze Audio è Video cù Cuntrolu Minimu) stabilisce parechji tippi di carica; a tavola currispondente furnisce una descrizzione di i tipi di carica è u significatu di i codici da quale sò designati. Certi codici ùn sò micca strettamente ligati à qualsiasi tipu di carica; ponu esse aduprati per designà una carica arbitraria.

A dimensione di un bloccu di dati hè limitata sopra da a dimensione massima di u pacchettu chì pò esse trasmessa in una reta data senza segmentazione (parametru MTU). In generale, questu hè micca più di 1500 bytes. Cusì, per aumentà a quantità di dati trasmessi per seconda, pudete aumentà a dimensione di u pacchettu finu à un certu puntu, è dopu avete bisognu di aumentà a freccia di mandà pacchetti. In un streamer media, questu hè un paràmetru configurabile. Per automaticamente hè 50 Hz, i.e. 50 pacchetti per seconda. Chjameremu a sequenza di pacchetti RTP trasmessi un flussu RTP.

Per inizià a trasmissione di dati trà a fonte è u receptore, hè abbastanza chì u trasmettitore cunnosce l'indirizzu IP di u receptore è u numeru di portu chì usa per riceve. Quelli. senza prucedure preliminari, a fonte principia à trasmette dati, è u receptore, à u turnu, hè prontu à riceve immediatamente è processà. Sicondu u standard, u numeru di portu utilizatu per trasmette o riceve un flussu RTP deve esse ancu.

In situazioni induve hè impussibile di cunnosce l'indirizzu di u receptore in anticipu, i servitori sò utilizati induve i receptori lascianu u so indirizzu, è u trasmettitore pò dumandà si riferenu à un nome unicu di u receptore.

In i casi induve a qualità di u canali di cumunicazione o e capacità di u receptore sò scunnisciuti, un canale di feedback hè urganizatu per mezu di quale u receptore pò informà u trasmettitore nantu à e so capacità, u numeru di pacchetti mancati, etc. Stu canale usa u protocolu RTCP. U formatu di pacchetti trasmessi in questu canale hè definitu in RFC 3605. Relativamente pocu dati sò trasmessi nantu à questu canale, 200..300 bytes per seconda, cusì in generale, a so prisenza ùn hè micca pesante. U numeru di portu à quale i pacchetti RTCP sò mandati deve esse stranu è unu più grande di u numeru di portu da quale vene u flussu RTP. In u nostru esempiu, ùn avemu micca aduprà stu canale, postu chì e capacità di u receptore è u canali ovviamente superanu i nostri bisogni, finu à quì modesti.

In u nostru prugramma, u circuitu di trasmissione di dati, à u cuntrariu di l'esempiu precedente, serà divisu in dui parti: una strada di trasmissione è una strada di ricezione. Per ogni parte faremu a nostra propria fonte di clock, cum'è mostra in a stampa di titulu.

A cumunicazione unidirezionale trà elli serà realizata cù u protocolu RTP. In questu esempiu, ùn avemu micca bisognu di una reta esterna, postu chì u trasmettitore è u receptore seranu situati in u stessu computer - i pacchetti viaghjanu in questu.

Per stabilisce un flussu RTP, u media streamer usa dui filtri: MS_RTP_SEND è MS_RTP_RECV. U primu trasmette u sicondu è riceve u flussu RTP. Per fà questi filtri per travaglià, anu bisognu di passà un punteru à un oggettu di sessione RTP, chì pò cunvertisce un flussu di blocchi di dati in un flussu di pacchetti RTP o fà u cuntrariu. Siccomu u formatu di dati internu di u media streamer ùn currisponde micca à u formatu di dati di u pacchettu RTP, prima di trasfiriri i dati à MS_RTP_SEND, avete bisognu di utilizà un filtru di codificatore chì cunverta campioni di segnali audio 16-bit in ottu-bit codificati secondu u u-law (mu-law). Da u latu di ricezione, u filtru di decodificatore svolge a funzione opposta.

Quì sottu hè u testu di u prugramma chì implementa u schema mostratu in a figura (i simboli # prima di e direttive include sò stati eliminati, ùn vi scurdate di includeli):

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

Cumpilemu è corremu. U prugramma hà da travaglià cum'è in l'esempiu precedente, ma i dati seranu trasmessi via un flussu RTP.

In u prossimu articulu, sparteremu stu prugramma in dui applicazioni indipendenti - un receptore è un trasmettitore è lanciamu in diverse terminali. À u listessu tempu, ampararemu à analizà i pacchetti RTP cù u prugramma TShark.

Source: www.habr.com

Add a comment