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 пакетуудыг хэрхэн шинжлэх талаар сурах болно.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх