สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 6

เนื้อหาของบทความนำมาจากของฉัน ช่องเซน.

การส่งสัญญาณเสียงผ่านสตรีม RTP

สำรวจเครื่องมือ Mediastreamer2 VoIP ส่วนที่ 6

ในที่สุด статье เราได้ประกอบวงจรควบคุมระยะไกลจากเครื่องกำเนิดเสียงและเครื่องตรวจจับเสียงที่ทำงานภายในโปรแกรมเดียวกัน ในบทความนี้เราจะเรียนรู้วิธีใช้โปรโตคอล RTP (RFC 3550 - RTP: โปรโตคอลการขนส่งสำหรับแอปพลิเคชันแบบเรียลไทม์) สำหรับการรับ/ส่งสัญญาณเสียงผ่านเครือข่ายอีเธอร์เน็ต

โปรโตคอล RTP (โปรโตคอลเรียลไทม์) แปล หมายถึง โปรโตคอลแบบเรียลไทม์ ใช้ในการส่งเสียง วิดีโอ ข้อมูล ทุกอย่างที่ต้องส่งแบบเรียลไทม์ ลองใช้สัญญาณเสียงเป็นตัวอย่าง ความยืดหยุ่นของโปรโตคอลทำให้คุณสามารถส่งสัญญาณเสียงด้วยคุณภาพที่กำหนดไว้ล่วงหน้าได้

การส่งข้อมูลดำเนินการโดยใช้แพ็กเก็ต UDP ซึ่งหมายความว่าการสูญเสียแพ็กเก็ตนั้นค่อนข้างยอมรับได้ในระหว่างการส่ง แต่ละแพ็กเก็ตประกอบด้วยส่วนหัว RTP พิเศษและบล็อกข้อมูลของสัญญาณที่ส่ง ส่วนหัวประกอบด้วยตัวระบุแหล่งสัญญาณที่เลือกแบบสุ่ม ข้อมูลเกี่ยวกับประเภทของสัญญาณที่ถูกส่ง และหมายเลขลำดับแพ็กเก็ตที่ไม่ซ้ำกัน เพื่อให้สามารถจัดเรียงแพ็กเก็ตในลำดับที่ถูกต้องเมื่อถอดรหัส โดยไม่คำนึงถึงลำดับที่พวกมันถูกส่งโดย เครือข่าย ส่วนหัวยังสามารถมีข้อมูลเพิ่มเติม ซึ่งเรียกว่าส่วนขยาย ซึ่งช่วยให้สามารถปรับส่วนหัวเพื่อใช้ในงานแอปพลิเคชันเฉพาะได้

บล็อกข้อมูลประกอบด้วยเพย์โหลดของแพ็กเก็ต การจัดระเบียบเนื้อหาภายในขึ้นอยู่กับประเภทของการโหลด อาจเป็นตัวอย่างของสัญญาณโมโน สัญญาณสเตอริโอ เส้นภาพวิดีโอ ฯลฯ

ประเภทการโหลดจะแสดงด้วยตัวเลขเจ็ดบิต คำแนะนำ RFC3551 (โปรไฟล์ RTP สำหรับการประชุมทางเสียงและวิดีโอพร้อมการควบคุมขั้นต่ำ) สร้างโหลดหลายประเภท ตารางที่เกี่ยวข้องให้คำอธิบายประเภทของโหลดและความหมายของรหัสที่กำหนด รหัสบางตัวไม่ได้เชื่อมโยงกับโหลดประเภทใด ๆ อย่างเคร่งครัด แต่สามารถใช้เพื่อกำหนดโหลดตามอำเภอใจได้

ขนาดของบล็อกข้อมูลถูกจำกัดไว้ด้านบนด้วยขนาดแพ็กเก็ตสูงสุดที่สามารถส่งบนเครือข่ายที่กำหนดโดยไม่มีการแบ่งส่วน (พารามิเตอร์ MTU) โดยทั่วไปจะมีขนาดไม่เกิน 1500 ไบต์ ดังนั้น เพื่อเพิ่มปริมาณข้อมูลที่ส่งต่อวินาที คุณสามารถเพิ่มขนาดแพ็คเก็ตได้ถึงจุดหนึ่ง จากนั้นคุณจะต้องเพิ่มความถี่ในการส่งแพ็คเก็ต ในสตรีมมีเดีย นี่คือการตั้งค่าที่สามารถกำหนดค่าได้ โดยค่าเริ่มต้นจะเป็น 50 Hz เช่น 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 บิตเป็น XNUMX บิตที่เข้ารหัสตาม ยู-ลอว์ (มู-ลอว์). ในด้านรับ ตัวกรองตัวถอดรหัสจะทำหน้าที่ตรงกันข้าม

ด้านล่างนี้เป็นข้อความของโปรแกรมที่ใช้โครงร่างดังรูป (สัญลักษณ์ # ก่อนคำสั่ง include จะถูกลบออก อย่าลืมรวมไว้ด้วย):

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

ในบทความถัดไป เราจะแบ่งโปรแกรมนี้ออกเป็นสองแอปพลิเคชันอิสระ - เครื่องรับและเครื่องส่งสัญญาณ และเปิดใช้งานในเทอร์มินัลที่แตกต่างกัน ในเวลาเดียวกันเราจะได้เรียนรู้วิธีวิเคราะห์แพ็กเก็ต RTP โดยใช้โปรแกรม TShark

ที่มา: will.com

เพิ่มความคิดเห็น