Mediastreamer2 VoIP mühərriki araşdırılır. 6-ci hissə

Məqalənin materialı məndən götürülmüşdür zen kanalı.

RTP axını vasitəsilə səs siqnalının ötürülməsi

Mediastreamer2 VoIP mühərriki araşdırılır. 6-ci hissə

Sonda məqalə Biz ton generatorundan və eyni proqram çərçivəsində işləyən ton detektorundan uzaqdan idarəetmə sxemi yığdıq. Bu yazıda RTP protokolundan necə istifadə edəcəyimizi öyrənəcəyik (RFC 3550 - RTP: Real-Time Tətbiqlər üçün Nəqliyyat Protokolu) Ethernet şəbəkəsi üzərindən audio siqnalın qəbulu/ötürülməsi üçün.

RTP protokolu (Real vaxt protokolu) tərcümə edilmiş real vaxt protokolu deməkdir, audio, video, məlumat, real vaxtda ötürülməni tələb edən hər şeyi ötürmək üçün istifadə olunur. Nümunə olaraq səs siqnalını götürək. Protokolun çevikliyi elədir ki, o, əvvəlcədən müəyyən edilmiş keyfiyyətlə səs siqnalını ötürməyə imkan verir.

Ötürülmə UDP paketlərindən istifadə etməklə həyata keçirilir, yəni ötürmə zamanı paket itkisi olduqca məqbuldur. Hər bir paketdə xüsusi RTP başlığı və ötürülən siqnalın məlumat bloku var. Başlıq təsadüfi seçilmiş siqnal mənbəyi identifikatorunu, ötürülən siqnalın növü haqqında məlumatı və unikal paket sıra nömrəsini ehtiva edir ki, paketlər dekodlaşdırma zamanı düzgün ardıcıllıqla düzülə bilsin. şəbəkə. Başlıq həmçinin əlavə məlumatı, sözdə uzantıdan ibarət ola bilər ki, bu da başlığı konkret proqram tapşırığında istifadə üçün uyğunlaşdırmağa imkan verir.

Məlumat bloku paketin faydalı yükünü ehtiva edir. Məzmunun daxili təşkili yükün növündən asılıdır, monosiqnal, stereo siqnal, video görüntü xətti və s. nümunələri ola bilər.

Yük növü yeddi bitlik rəqəmlə göstərilir. Tövsiyə RFC3551 (Minimal İdarəetmə ilə Audio və Video Konfranslar üçün RTP Profili) bir neçə növ yük təyin edir; müvafiq cədvəldə yük növlərinin təsviri və onların təyin olunduğu kodların mənası verilir. Bəzi kodlar hər hansı bir yük növünə ciddi şəkildə bağlı deyil, onlar ixtiyari bir yük təyin etmək üçün istifadə edilə bilər.

Məlumat blokunun ölçüsü yuxarıda seqmentləşdirmə olmadan verilmiş şəbəkədə ötürülə bilən maksimum paket ölçüsü ilə məhdudlaşır (MTU parametri). Ümumiyyətlə, bu 1500 baytdan çox deyil. Beləliklə, saniyədə ötürülən məlumatların miqdarını artırmaq üçün siz paket ölçüsünü müəyyən bir nöqtəyə qədər artıra bilərsiniz, sonra isə paketlərin göndərilmə tezliyini artırmaq lazımdır. Media yayımlayıcıda bu, konfiqurasiya edilə bilən parametrdir. Varsayılan olaraq 50 Hz-dir, yəni. Saniyədə 50 paket. Biz ötürülən RTP paketlərinin ardıcıllığını RTP axını adlandıracağıq.

Mənbə və qəbuledici arasında məlumat ötürməyə başlamaq üçün ötürücünün qəbuledicinin IP ünvanını və qəbul etmək üçün istifadə etdiyi port nömrəsini bilməsi kifayətdir. Bunlar. heç bir ilkin prosedurlar olmadan mənbə məlumatları ötürməyə başlayır və qəbuledici də öz növbəsində onu dərhal qəbul etməyə və emal etməyə hazırdır. Standarta görə, RTP axınını ötürmək və ya qəbul etmək üçün istifadə edilən port nömrəsi cüt olmalıdır.

Qəbuledicinin ünvanını əvvəlcədən bilmək mümkün olmayan hallarda, qəbuledicilərin ünvanlarını tərk etdikləri serverlərdən istifadə olunur və ötürücü alıcının bəzi unikal adına istinad edərək bunu tələb edə bilər.

Rabitə kanalının keyfiyyəti və ya qəbuledicinin imkanları məlum olmayan hallarda, əks əlaqə kanalı təşkil edilir ki, onun vasitəsilə qəbuledici ötürücüyə öz imkanları, qaçırdığı paketlərin sayı və s. Bu kanal RTCP protokolundan istifadə edir. Bu kanalda ötürülən paketlərin formatı RFC 3605-də müəyyən edilmişdir. Bu kanal üzərindən nisbətən az məlumat ötürülür, saniyədə 200..300 bayt, ona görə də ümumilikdə onun mövcudluğu ağır deyil. RTCP paketlərinin göndərildiyi port nömrəsi tək və RTP axınının gəldiyi port nömrəsindən bir böyük olmalıdır. Nümunəmizdə bu kanaldan istifadə etməyəcəyik, çünki qəbuledicinin və kanalın imkanları açıq şəkildə bizim ehtiyaclarımızı üstələyir.

Proqramımızda məlumat ötürmə sxemi, əvvəlki nümunədən fərqli olaraq, iki hissəyə bölünəcəkdir: ötürmə yolu və qəbul yolu. Hər hissə üçün başlıq şəklində göstərildiyi kimi öz saat mənbəyimizi edəcəyik.

Onlar arasında birtərəfli əlaqə RTP protokolundan istifadə etməklə həyata keçiriləcək. Bu misalda bizə xarici şəbəkə lazım deyil, çünki həm ötürücü, həm də qəbuledici eyni kompüterdə yerləşəcək - paketlər onun daxilində hərəkət edəcək.

RTP axını yaratmaq üçün media yayımlayıcı iki filtrdən istifadə edir: MS_RTP_SEND və MS_RTP_RECV. Birincisi ikincisini ötürür və RTP axını alır. Bu filtrlərin işləməsi üçün onlar ya məlumat blokları axınını RTP paketləri axınına çevirə və ya əksinə edə bilən RTP sessiya obyektinə göstərici ötürməlidirlər. Media yayımlayıcısının daxili məlumat formatı RTP paketinin məlumat formatına uyğun gəlmədiyi üçün məlumatları MS_RTP_SEND-ə ötürməzdən əvvəl, 16 bitlik audio siqnal nümunələrini səkkiz bitlik kodlaşdırılmış formata çevirən kodlayıcı filtrdən istifadə etməlisiniz. u-qanun (mu-law). Qəbul edən tərəfdə dekoder filtri əks funksiyanı yerinə yetirir.

Aşağıda şəkildə göstərilən sxemi həyata keçirən proqramın mətni verilmişdir (include direktivlərindən əvvəl # simvollar silinmişdir, onları daxil etməyi unutmayın):

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

Biz tərtib edirik və işə salırıq. Proqram əvvəlki nümunədə olduğu kimi işləyəcək, lakin məlumatlar RTP axını vasitəsilə ötürüləcək.

Növbəti məqalədə bu proqramı iki müstəqil proqrama - qəbuledici və ötürücüyə bölüb müxtəlif terminallarda işə salacağıq. Eyni zamanda, TShark proqramından istifadə edərək RTP paketlərinin təhlilini öyrənəcəyik.

Mənbə: www.habr.com

Добавить комментарий