Mediastreamer2 VoIP dvigatelini o'rganish. 9-qism

Maqolaning materiali mendan olingan zen kanali.

Dupleks interkom

Mediastreamer2 VoIP dvigatelini o'rganish. 9-qism

Oxirida maqola dupleks interkom e'lon qilindi va biz buni qilamiz.

Diagramma sarlavha rasmida ko'rsatilgan. Filtrlarning pastki zanjiri ovoz kartasidan boshlanadigan uzatish yo'lini tashkil qiladi. U mikrofondan signal namunalarini beradi. Odatiy bo'lib, bu soniyada 8000 namuna tezligida sodir bo'ladi. Media streamer audio filtrlari foydalanadigan ma'lumotlar bit chuqurligi 16 bit (bu muhim emas, agar xohlasangiz, yuqori bit chuqurligi bilan ishlaydigan filtrlarni yozishingiz mumkin). Ma'lumotlar 160 ta namunadan iborat bloklarga guruhlangan. Shunday qilib, har bir blok 320 bayt hajmga ega. Keyinchalik, biz ma'lumotlarni generatorning kirishiga yuboramiz, u o'chirilganda ma'lumotlar uchun "shaffof" bo'ladi. Nosozliklarni tuzatish paytida mikrofon bilan gaplashishdan charchagan bo'lsangiz, men uni qo'shdim - siz ohang signali bilan yo'lni "otish" uchun generatordan foydalanishingiz mumkin.

Generatordan so'ng signal m-qonuniga (G.16 standarti) muvofiq 711 bitli namunalarimizni sakkiz bitlilarga aylantiradigan kodlovchiga o'tadi. Kodlovchining chiqishida biz allaqachon yarim o'lchamdagi ma'lumotlar blokiga egamiz. Umuman olganda, agar biz trafikni tejashimiz kerak bo'lmasa, biz ma'lumotlarni siqmasdan uzatishimiz mumkin. Ammo bu erda kodlovchidan foydalanish foydalidir, chunki Wireshark RTP oqimidan faqat µ-qonun yoki qonunga muvofiq siqilganida audioni qayta ishlab chiqarishi mumkin.

Kodlovchidan so'ng ma'lumotlarning engil bloklari rtpsend filtriga yuboriladi, u ularni RTP paketiga joylashtiradi, kerakli bayroqlarni o'rnatadi va ularni UDP paketi ko'rinishida tarmoq orqali uzatish uchun media strimerga beradi.

Filtrlarning yuqori zanjiri qabul qilish yo'lini tashkil qiladi; tarmoqdan media strimeri tomonidan qabul qilingan RTP paketlari rtprecv filtriga kiradi, ularning chiqishida ular ma'lumotlar bloklari ko'rinishida paydo bo'ladi, ularning har biri bitta qabul qilingan paketga to'g'ri keladi. Blok faqat foydali yuk ma'lumotlarini o'z ichiga oladi, avvalgi maqolada ular rasmda yashil rangda ko'rsatilgan.

Keyinchalik, bloklar dekoder filtriga yuboriladi, ular tarkibidagi bir baytli namunalarni chiziqli, 16 bitlilarga aylantiradi. Buni allaqachon media strimer filtrlari bilan qayta ishlash mumkin. Bizning holatda, biz ularni eshitish vositasining karnaylarida o'ynash uchun shunchaki ovoz kartasiga yuboramiz.

Endi dasturiy ta'minotni amalga oshirishga o'tamiz. Buning uchun biz avval ajratgan qabul qiluvchi va uzatuvchi fayllarni birlashtiramiz. Bundan oldin biz portlar va manzillar uchun sobit sozlamalardan foydalanganmiz, ammo endi biz ishga tushirishda ko'rsatgan sozlamalardan foydalanishimiz uchun dasturga muhtojmiz. Buning uchun biz buyruq qatori argumentlarini qayta ishlash funksiyasini qo'shamiz. Shundan so'ng biz ulanishni o'rnatmoqchi bo'lgan interkomning IP-manzilini va portini o'rnatishimiz mumkin.

Birinchidan, dasturga uning sozlamalarini saqlaydigan tuzilmani qo'shamiz:

struct _app_vars
{
  int  local_port;              /* Локальный порт. */
  int  remote_port;             /* Порт переговорного устройства на удаленном компьютере. */
  char remote_addr[128];        /* IP-адрес удаленного компьютера. */
  MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};

typedef struct _app_vars app_vars;

Dastur vars deb nomlangan ushbu turdagi strukturani e'lon qiladi.
Keyin buyruq qatori argumentlarini tahlil qilish funksiyasini qo'shamiz:

/* Функция преобразования аргументов командной строки в
* настройки программы. */
void  scan_args(int argc, char *argv[], app_vars *v)
{
    char i;
    for (i=0; i<argc; i++)
    {
        if (!strcmp(argv[i], "--help"))
        {
            char *p=argv[0]; p=p + 2;
            printf("  %s walkie talkienn", p);
            printf("--help      List of options.n");
            printf("--version   Version of application.n");
            printf("--addr      Remote abonent IP address string.n");
            printf("--port      Remote abonent port number.n");
            printf("--lport     Local port number.n");
            printf("--gen       Generator frequency.n");
            exit(0);
        }

        if (!strcmp(argv[i], "--version"))
        {
            printf("0.1n");
            exit(0);
        }

        if (!strcmp(argv[i], "--addr"))
        {
            strncpy(v->remote_addr, argv[i+1], 16);
            v->remote_addr[16]=0;
            printf("remote addr: %sn", v->remote_addr);
        }

        if (!strcmp(argv[i], "--port"))
        {
            v->remote_port=atoi(argv[i+1]);
            printf("remote port: %in", v->remote_port);
        }

        if (!strcmp(argv[i], "--lport"))
        {
            v->local_port=atoi(argv[i+1]);
            printf("local port : %in", v->local_port);
        }

        if (!strcmp(argv[i], "--gen"))
        {
            v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
                printf("gen freq : %in", v -> dtmf_cfg.frequencies[0]);
        }
    }
}

Tahlil qilish natijasida buyruq qatori argumentlari vars strukturasining maydonlariga joylashtiriladi. Ilovaning asosiy vazifasi filtrlardan uzatish va qabul qilish yo'llarini to'plashdan iborat bo'ladi; tickerni ulagandan so'ng, boshqaruv cheksiz tsiklga o'tkaziladi, agar generator chastotasi nolga teng bo'lmagan bo'lsa, sinov generatorini qayta ishga tushiradi. to'xtovsiz ishlaydi.

Jeneratör o'zining dizayni tufayli ushbu qayta ishga tushirishga muhtoj bo'ladi; ba'zi sabablarga ko'ra u 16 soniyadan ortiq davom etadigan signalni ishlab chiqara olmaydi. Shuni ta'kidlash kerakki, uning davomiyligi 32 bitli raqam bilan belgilanadi.

Butun dastur quyidagicha ko'rinadi:

/* Файл mstest8.c Имитатор переговорного устройства. */

#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>

/* Подключаем файл общих функций. */
#include "mstest_common.c"

/*----------------------------------------------------------*/
struct _app_vars
{
    int  local_port;              /* Локальный порт. */
    int  remote_port;             /* Порт переговорного устройства на удаленном компьютере. */
    char remote_addr[128];        /* IP-адрес удаленного компьютера. */
    MSDtmfGenCustomTone dtmf_cfg; /* Настройки тестового сигнала генератора. */
};

typedef struct _app_vars app_vars;

/*----------------------------------------------------------*/
/* Создаем дуплексную RTP-сессию. */
RtpSession* create_duplex_rtp_session(app_vars v)
{
    RtpSession *session = create_rtpsession (v.local_port, v.local_port + 1, FALSE, RTP_SESSION_SENDRECV);
    rtp_session_set_remote_addr_and_port(session, v.remote_addr, v.remote_port, v.remote_port + 1);
    rtp_session_set_send_payload_type(session, PCMU);
    return session;
}

/*----------------------------------------------------------*/
/* Функция преобразования аргументов командной строки в
* настройки программы. */
void  scan_args(int argc, char *argv[], app_vars *v)
{
    char i;
    for (i=0; i<argc; i++)
    {
        if (!strcmp(argv[i], "--help"))
        {
            char *p=argv[0]; p=p + 2;
            printf("  %s walkie talkienn", p);
            printf("--help      List of options.n");
            printf("--version   Version of application.n");
            printf("--addr      Remote abonent IP address string.n");
            printf("--port      Remote abonent port number.n");
            printf("--lport     Local port number.n");
            printf("--gen       Generator frequency.n");
            exit(0);
        }

        if (!strcmp(argv[i], "--version"))
        {
            printf("0.1n");
            exit(0);
        }

        if (!strcmp(argv[i], "--addr"))
        {
            strncpy(v->remote_addr, argv[i+1], 16);
            v->remote_addr[16]=0;
            printf("remote addr: %sn", v->remote_addr);
        }

        if (!strcmp(argv[i], "--port"))
        {
            v->remote_port=atoi(argv[i+1]);
            printf("remote port: %in", v->remote_port);
        }

        if (!strcmp(argv[i], "--lport"))
        {
            v->local_port=atoi(argv[i+1]);
            printf("local port : %in", v->local_port);
        }

        if (!strcmp(argv[i], "--gen"))
        {
            v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
                printf("gen freq : %in", v -> dtmf_cfg.frequencies[0]);
        }
    }
}

/*----------------------------------------------------------*/
int main(int argc, char *argv[])
{
    /* Устанавливаем настройки по умолчанию. */
    app_vars vars={5004, 7010, "127.0.0.1", {0}};

    /* Устанавливаем настройки настройки программы в
     * соответствии с аргументами командной строки. */
    scan_args(argc, argv, &vars);

    ms_init();

    /* Создаем экземпляры фильтров передающего тракта. */
    MSSndCard *snd_card =
        ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
    MSFilter *snd_card_read = ms_snd_card_create_reader(snd_card);
    MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
    MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID);

    /* Создаем фильтр кодера. */
    MSFilter *encoder = ms_filter_create_encoder("PCMU");

    /* Регистрируем типы нагрузки. */
    register_payloads();

    /* Создаем дуплексную RTP-сессию. */
    RtpSession* rtp_session= create_duplex_rtp_session(vars);
    ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, rtp_session);

    /* Соединяем фильтры передатчика. */
    ms_filter_link(snd_card_read, 0, dtmfgen, 0);
    ms_filter_link(dtmfgen, 0, encoder, 0);
    ms_filter_link(encoder, 0, rtpsend, 0);

    /* Создаем фильтры приемного тракта. */
    MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
    ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rtp_session);

    /* Создаем фильтр декодера, */
    MSFilter *decoder=ms_filter_create_decoder("PCMU");

    /* Создаем фильтр звуковой карты. */
    MSFilter *snd_card_write = ms_snd_card_create_writer(snd_card);

    /* Соединяем фильтры приёмного тракта. */
    ms_filter_link(rtprecv, 0, decoder, 0);
    ms_filter_link(decoder, 0,  snd_card_write, 0);

    /* Создаем источник тактов - тикер. */
    MSTicker *ticker = ms_ticker_new();

    /* Подключаем источник тактов. */
    ms_ticker_attach(ticker, snd_card_read);
    ms_ticker_attach(ticker, rtprecv);

    /* Если настройка частоты генератора отлична от нуля, то запускаем генератор. */   
    if (vars.dtmf_cfg.frequencies[0])
    {
        /* Настраиваем структуру, управляющую выходным сигналом генератора. */
        vars.dtmf_cfg.duration = 10000;
        vars.dtmf_cfg.amplitude = 1.0;
    }

    /* Организуем цикл перезапуска генератора. */
    while(TRUE)
    {
        if(vars.dtmf_cfg.frequencies[0])
        {
            /* Включаем звуковой генератор. */
            ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
                    (void*)&vars.dtmf_cfg);
        }
        /* Укладываем тред в спячку на 20мс, чтобы другие треды
         * приложения получили время на работу. */
        ms_usleep(20000);
    }
}

Keling, kompilyatsiya qilaylik. Keyin dastur ikkita kompyuterda ishlashi mumkin. Yoki bittasida, men hozir qilaman. Biz TSharkni quyidagi argumentlar bilan ishga tushiramiz:

$ sudo tshark -i lo -f "udp dst port 7010" -P -V -O RTP -o rtp.heuristic_rtp:TRUE -x

Agar konsoldagi ishga tushirish maydoni faqat suratga olish boshlanishi haqida xabarni ko'rsatsa, bu yaxshi belgidir - bu bizning portimizni boshqa dasturlar egallamaganligini anglatadi. Boshqa terminalda biz ushbu port raqamini ko'rsatib, "masofaviy" interkomni taqlid qiladigan dastur namunasini ishga tushiramiz:

$ ./mstest8 --port 9010 --lport 7010

Dastur matnidan ko'rinib turibdiki, standart IP-manzil 127.0.0.1 (mahalliy loopback).

Boshqa terminalda biz dasturning ikkinchi nusxasini ishga tushiramiz, u mahalliy qurilmani simulyatsiya qiladi. Biz o'rnatilgan test generatorining ishlashiga imkon beruvchi qo'shimcha argumentdan foydalanamiz:

$ ./mstest8  --port 7010 --lport 9010 --gen 440

Ayni paytda "masofaviy" qurilmaga uzatiladigan paketlar TShark bilan konsolda miltillay boshlashi kerak va kompyuter karnayidan uzluksiz ohang eshitiladi.

Agar hamma narsa yozilganidek sodir bo'lsa, biz dasturning ikkinchi nusxasini qayta ishga tushiramiz, lekin kalit va argumentsiz "-gen 440". Endi siz generator rolini o'ynaysiz. Shundan so'ng siz mikrofonga shovqin qilishingiz mumkin, karnay yoki minigarniturada mos keladigan ovozni eshitishingiz kerak. O'z-o'zidan akustik qo'zg'alish ham paydo bo'lishi mumkin; karnay ovozini pasaytiring va effekt yo'qoladi.

Agar siz uni ikkita kompyuterda ishga tushirgan bo'lsangiz va IP-manzillar haqida adashmagan bo'lsangiz, sizni xuddi shu natija kutmoqda - ikki tomonlama raqamli sifatli ovozli aloqa.

Keyingi maqolada biz o'z filtrlarimizni - plaginlarni qanday yozishni o'rganamiz, bu mahorat tufayli siz media-strimerdan nafaqat audio va video uchun, balki boshqa muayyan sohalarda ham foydalanishingiz mumkin.

Manba: www.habr.com

a Izoh qo'shish