Mediastreamer2 VoIP кыймылдаткычын изилдөө. 6-бөлүк

Макаланын материалы менин zen каналы.

RTP агымы аркылуу аудио сигналды берүү

Mediastreamer2 VoIP кыймылдаткычын изилдөө. 6-бөлүк

Акырында макала Биз тон генераторунан жана бир эле программанын алкагында иштеген тон детекторунан алыстан башкаруу схемасын чогулттук. Бул макалада биз RTP протоколун кантип колдонууну үйрөнөбүз (RFC 3550 - RTP: реалдуу убакыттагы колдонмолор үчүн транспорттук протокол) Ethernet тармагы аркылуу аудио сигналды кабыл алуу/өткөрүү үчүн.

RTP протоколу (Реалдуу убакыт протоколу) которулган реалдуу убакыт протоколун билдирет, ал аудио, видео, маалыматтарды, реалдуу убакытта берүүнү талап кылган нерселердин бардыгын берүү үчүн колдонулат. Мисал катары аудио сигналды алалы. Протоколдун ийкемдүүлүгү, ал алдын ала белгиленген сапаттагы аудио сигналды берүүгө мүмкүндүк берет.

Берүү UDP пакеттерин колдонуу менен ишке ашат, бул берүү учурунда пакеттин жоголушу алгылыктуу экенин билдирет. Ар бир пакет атайын RTP башын жана берилүүчү сигналдын маалымат блогун камтыйт. Баш маалымат кокусунан тандалып алынган сигнал булагынын идентификаторун, берилүүчү сигналдын түрү жөнүндө маалыматты жана пакеттердин уникалдуу ырааттуу номерин камтыйт, ошентип пакеттер аларды жеткирүү тартибине карабастан, декоддоодо туура тартипте жайгаштырылышы мүмкүн. тармак. Баш аты кошумча маалыматты камтышы мүмкүн, бул кеңейтүү деп аталган, ал башты белгилүү бир колдонмо тапшырмасында колдонууга ылайыкташтырууга мүмкүндүк берет.

Маалымат блогу пакеттин пайдалуу жүгүн камтыйт. Мазмундун ички уюштурулушу жүктүн түрүнө жараша болот, ал моносигналдын үлгүлөрү, стерео сигнал, видео сүрөт линиясы ж.б.

Жүктүн түрү жети биттик сан менен көрсөтүлөт. Сунуш RFC3551 (Минималдуу башкаруу менен аудио жана видео конференциялар үчүн RTP профили) жүктүн бир нече түрлөрүн белгилейт, тиешелүү таблицада жүктүн түрлөрүнүн сүрөттөлүшү жана алар дайындалган коддордун мааниси берилген. Кээ бир коддор жүктүн кандайдыр бир түрү менен тыгыз байланышта эмес, алар ыктыярдуу жүктү белгилөө үчүн колдонулушу мүмкүн.

Берилиштер блогунун өлчөмү жогоруда сегментациясыз берилген тармакта берилүүчү пакеттин максималдуу өлчөмү менен чектелген (MTU параметри). Жалпысынан алганда, бул 1500 байттан ашпайт. Ошентип, секундасына берилүүчү маалыматтардын көлөмүн көбөйтүү үчүн, пакеттин өлчөмүн белгилүү бир чекитке чейин көбөйтүүгө болот, андан кийин пакеттерди жөнөтүү жыштыгын көбөйтүү керек болот. Медиа агымда бул конфигурациялануучу жөндөө. Демейки боюнча ал 50 Гц, б.а. секундасына 50 пакет. Берилген RTP пакеттеринин ырааттуулугун RTP агымы деп атайбыз.

Булак менен кабыл алгычтын ортосунда маалыматтарды өткөрүп баштоо үчүн, өткөргүч ресивердин IP дарегин жана кабыл алуу үчүн колдонгон порт номерин билиши жетиштүү. Ошол. эч кандай алдын ала жол-жоболору жок, булак маалыматтарды бере баштайт, ал эми кабыл алуучу, өз кезегинде, дароо кабыл алууга жана аны иштеп чыгууга даяр. Стандартка ылайык, RTP агымын берүү же кабыл алуу үчүн колдонулган порт номери жуп болушу керек.

Кабыл алуучунун дарегин алдын ала билүү мүмкүн болбогон учурларда, серверлер ресивер өз дарегин таштап кетүүчү жерде колдонулат, ал эми жөнөтүүчү кабыл алуучунун кандайдыр бир уникалдуу аталышына шилтеме берүү менен аны сурай алат.

Байланыш каналынын сапаты же ресивердин мүмкүнчүлүктөрү белгисиз болгон учурларда, кайтарым байланыш каналы уюштурулат, ал аркылуу кабыл алуучу өзүнүн мүмкүнчүлүктөрү, өткөрүп жиберген пакеттердин саны ж.б. Бул канал RTCP протоколун колдонот. Бул каналда берилүүчү пакеттердин форматы RFC 3605те аныкталган. Бул канал аркылуу салыштырмалуу аз маалымат берилет, секундасына 200..300 байт, ошондуктан жалпысынан анын болушу оор эмес. RTCP пакеттери жөнөтүлгөн порт номери так жана RTP агымы келген порт номеринен бир чоңураак болушу керек. Биздин мисалда биз бул каналды колдонбойбуз, анткени ресивердин жана каналдын мүмкүнчүлүктөрү биздин, азырынча жөнөкөй муктаждыктарыбыздан ашат.

Биздин программада маалыматтарды берүү схемасы, мурунку мисалдан айырмаланып, эки бөлүккө бөлүнөт: берүү жолу жана кабыл алуу жолу. Ар бир бөлүк үчүн биз аталыштагы сүрөттө көрсөтүлгөндөй, өзүбүздүн саат булагыбызды жасайбыз.

Алардын ортосундагы бир тараптуу байланыш RTP протоколу аркылуу ишке ашырылат. Бул мисалда бизге тышкы тармактын кереги жок, анткени өткөргүч да, кабыл алгыч да бир компьютерде жайгашат - пакеттер анын ичинде жүрөт.

RTP агымын түзүү үчүн, медиа агымчы эки чыпканы колдонот: MS_RTP_SEND жана MS_RTP_RECV. Биринчиси экинчисин өткөрүп, RTP агымын алат. Бул чыпкалар иштеши үчүн, алар RTP сеанс объектисине көрсөткүчтү өткөрүп бериши керек, ал маалымат блокторунун агымын RTP пакеттеринин агымына айландырышы же тескерисинче жасай алат. Медиа стримердин ички маалымат форматы RTP пакетинин маалымат форматына дал келбегендиктен, маалыматтарды MS_RTP_SENDге өткөрүүдөн мурун, сиз 16 биттик аудио сигнал үлгүлөрүн төмөнкүгө ылайык коддолгон сегиз биттикке айландырган коддоочу чыпканы колдонушуңуз керек. у-мыйзам (му-мыйзам). Кабыл алуучу тарапта декодер чыпкасы карама-каршы функцияны аткарат.

Төмөндө сүрөттө көрсөтүлгөн схеманы ишке ашырган программанын тексти келтирилген (кошуу директивасына чейинки # символдор алынып салынган, аларды кошууну унутпаңыз):

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

Биз түзөбүз жана иштетебиз. Программа мурунку мисалдагыдай иштейт, бирок маалыматтар RTP агымы аркылуу берилет.

Кийинки макалада биз бул программаны эки көз карандысыз тиркемелерге - кабыл алгыч жана өткөргүчкө бөлүп, аларды ар кандай терминалдарда ишке киргизебиз. Ошол эле учурда, биз TShark программасын колдонуу менен RTP пакеттерин талдоону үйрөнөбүз.

Source: www.habr.com

Комментарий кошуу