Ngajalajah mesin Mediastreamer2 VoIP. Bagian 6

Materi artikel dicokot ti abdi saluran zen.

Mancarkeun sinyal audio ngaliwatan aliran RTP

Ngajalajah mesin Mediastreamer2 VoIP. Bagian 6

Di tempo anu kalangkung artikel Kami parantos ngarakit sirkuit kadali jauh tina generator nada sareng detektor nada anu beroperasi dina program anu sami. Dina tulisan ieu urang bakal diajar kumaha ngagunakeun protokol RTP (RFC 3550 - RTP: Protokol Angkutan pikeun Aplikasi Real-Time) pikeun narima / ngirimkeun sinyal audio ngaliwatan jaringan Ethernet.

protokol RTP (Protokol Real Time) ditarjamahkeun hartina protokol real-time, dipaké pikeun ngirimkeun audio, video, data, sagalana nu merlukeun transmisi sacara real waktu. Hayu urang nyandak sinyal audio sabagé conto. Kalenturan protokol sapertos kitu ngamungkinkeun anjeun ngirimkeun sinyal audio kalayan kualitas anu tos ditangtukeun.

Pangiriman dilaksanakeun nganggo pakét UDP, anu hartosna leungitna pakét tiasa ditampi nalika pangiriman. Unggal pakét ngandung lulugu RTP husus sarta blok data tina sinyal dikirimkeun. Header ngandung identifier sumber sinyal anu dipilih sacara acak, inpormasi ngeunaan jinis sinyal anu dikirimkeun, sareng nomer urutan pakét anu unik supados pakét tiasa disusun dina urutan anu leres nalika decoding, henteu paduli urutan anu dikirimkeun ku pakét. jaringan. Lulugu ogé bisa ngandung émbaran tambahan, nu disebut extension, nu ngidinan lulugu diadaptasi pikeun pamakéan dina tugas aplikasi husus.

Blok data ngandung muatan tina pakét. Organisasi internal eusi gumantung kana jinis beban, tiasa janten conto sinyal mono, sinyal stereo, garis gambar video, jsb.

Jinis beban dituduhkeun ku angka tujuh-bit. Rekomendasi RFC3551 (Profil RTP pikeun Konperénsi Audio sareng Pidéo sareng Kontrol Minimal) netepkeun sababaraha jinis beban; tabél anu saluyu nyayogikeun pedaran ngeunaan jinis beban sareng hartos kodeu anu ditunjuk. Sababaraha kode henteu dikaitkeun sacara ketat kana jinis beban naon waé; aranjeunna tiasa dianggo pikeun nunjuk beban anu sawenang.

Ukuran blok data diwatesan di luhur ku ukuran pakét maksimum nu bisa dikirimkeun dina jaringan tinangtu tanpa segmentation (parameter MTU). Sacara umum, ieu henteu langkung ti 1500 bait. Ku kituna, dina raraga ngaronjatkeun jumlah data dikirimkeun per detik, Anjeun bisa ningkatkeun ukuran pakét nepi ka titik nu tangtu, lajeng anjeun bakal kudu ningkatkeun frékuénsi ngirim pakét. Dina streamer média, ieu mangrupikeun setélan anu tiasa dikonfigurasi. Sacara standar éta 50 Hz, i.e. 50 pakét per detik. Urang bakal nelepon runtuyan pakét RTP dikirimkeun aliran RTP.

Pikeun ngamimitian ngirimkeun data antara sumber sareng panarima, cukup ku pamancar terang alamat IP tina panarima sareng nomer port anu dianggo pikeun nampi. Jelema. Tanpa prosedur awal, sumberna mimiti ngirimkeun data, sareng panarima, kahareupna siap langsung nampi sareng ngolahna. Numutkeun standar, jumlah port anu dianggo pikeun ngirimkeun atanapi nampi aliran RTP kedah sami.

Dina kaayaan dimana teu mungkin uninga alamat panarima sateuacanna, server dipaké dimana panarima ninggalkeun alamatna, sarta pamancar bisa menta eta ku ngarujuk kana sababaraha ngaran unik tina panarima.

Dina kasus dimana kualitas saluran komunikasi atanapi kamampuan panarima henteu dipikanyaho, saluran eupan balik diatur dimana panarima tiasa nginpokeun ka pamancar ngeunaan kamampuanana, jumlah pakét anu lasut, jsb. Saluran ieu nganggo protokol RTCP. Format pakét dikirimkeun dina saluran ieu didefinisikeun dina RFC 3605. Relatif saeutik data dikirimkeun ngaliwatan channel ieu, 200..300 bait per detik, jadi sacara umum, ayana teu burdensome. Nomer port anu dikirimkeun pakét RTCP kedah ganjil sareng hiji langkung ageung tibatan nomer port dimana aliran RTP asalna. Dina conto urang, urang moal nganggo saluran ieu, sabab kamampuan panarima sareng saluran écés ngaleuwihan kabutuhan urang, dugi ka ayeuna.

Dina program urang, sirkuit pangiriman data, teu saperti conto saméméhna, bakal dibagi jadi dua bagian: jalur pangiriman jeung jalur panarimaan. Pikeun unggal bagian urang bakal nyieun sumber jam sorangan, ditémbongkeun saperti dina gambar judul.

Komunikasi saarah antara aranjeunna bakal dilaksanakeun nganggo protokol RTP. Dina conto ieu, urang henteu peryogi jaringan éksternal, sabab pemancar sareng panarima bakal aya dina komputer anu sami - pakét bakal ngarambat di jerona.

Pikeun nyieun aliran RTP, streamer média ngagunakeun dua saringan: MS_RTP_SEND jeung MS_RTP_RECV. Anu kahiji ngirimkeun anu kadua sareng nampi aliran RTP. Supados saringan ieu tiasa dianggo, aranjeunna kedah ngalebetkeun pointer ka obyék sési RTP, anu tiasa ngarobih aliran blok data kana aliran pakét RTP atanapi ngalakukeun sabalikna. Kusabab format data internal streamer média henteu cocog sareng format data pakét RTP, sateuacan nransferkeun data ka MS_RTP_SEND, anjeun kedah nganggo saringan encoder anu ngarobih conto sinyal audio 16-bit kana dalapan bit anu disandikeun dumasar kana u-law (mu-law). Di sisi panarima, saringan decoder ngalaksanakeun fungsi sabalikna.

Di handap ieu mangrupikeun téks program anu ngalaksanakeun skéma anu dipidangkeun dina gambar (simbol # sateuacan diréktif kalebet parantos dihapus, ulah hilap kalebet):

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

Urang compile jeung ngajalankeun. Program bakal tiasa dianggo sapertos dina conto sateuacana, tapi datana bakal dikirimkeun ku aliran RTP.

Dina artikel salajengna urang bakal ngabagi program ieu kana dua aplikasi mandiri - panarima sareng pamancar sareng ngaluncurkeunana dina terminal anu béda. Dina waktos anu sami, urang bakal diajar kumaha nganalisis pakét RTP nganggo program TShark.

sumber: www.habr.com

Tambahkeun komentar