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

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

Dupleks interkom

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

Sonda məqalə dupleks interkom elan edildi və biz bunu edəcəyik.

Diaqram başlıq şəklində göstərilmişdir. Filtrlərin aşağı zənciri səs kartından başlayan ötürmə yolunu təşkil edir. Mikrofondan siqnal nümunələri təqdim edir. Varsayılan olaraq, bu, saniyədə 8000 nümunə sürətində baş verir. Media streamer audio filtrlərinin istifadə etdiyi məlumat bit dərinliyi 16 bitdir (bu vacib deyil; istəsəniz, daha yüksək bit dərinliyi ilə işləyəcək filtrlər yaza bilərsiniz). Məlumatlar 160 nümunədən ibarət bloklarda qruplaşdırılıb. Beləliklə, hər blokun ölçüsü 320 baytdır. Sonra, məlumatları generatorun girişinə veririk, söndürüldükdə məlumatlar üçün "şəffaf" olur. Sazlama zamanı mikrofonla danışmaqdan bezdiyiniz halda əlavə etdim - bir ton siqnalı ilə yolu "atmaq" üçün generatordan istifadə edə bilərsiniz.

Generatordan sonra siqnal µ-qanununa (G.16 standartı) uyğun olaraq 711 bitlik nümunələrimizi səkkiz bitlik nümunələrə çevirən kodlayıcıya keçir. Kodlayıcının çıxışında artıq yarım ölçülü məlumat blokumuz var. Ümumiyyətlə, trafikə qənaət etmək lazım deyilsə, məlumatları sıxılmadan ötürə bilərik. Ancaq burada kodlayıcıdan istifadə etmək faydalıdır, çünki Wireshark RTP axınından səsi yalnız µ-qanununa və ya qanuna uyğun olaraq sıxıldığı zaman təkrar edə bilər.

Kodlayıcıdan sonra daha yüngül məlumat blokları rtpsend filtrinə göndərilir ki, bu da onları RTP paketinə yerləşdirəcək, lazımi bayraqları təyin edəcək və şəbəkə üzərindən UDP paketi şəklində ötürülməsi üçün media axınına verəcək.

Üst filtrlər zənciri qəbul yolunu təşkil edir; şəbəkədən media yayımçısı tərəfindən qəbul edilən RTP paketləri rtprecv filtrinə daxil olur, onların çıxışında hər biri bir qəbul paketinə uyğun gələn məlumat blokları şəklində görünür. Blok yalnız faydalı yük məlumatlarını ehtiva edir, əvvəlki məqalədə onlar təsvirdə yaşıl rəngdə göstərilmişdir.

Bundan sonra, bloklar onlarda olan bir baytlıq nümunələri xətti, 16 bitlik olanlara çevirən dekoder filtrinə göndərilir. Hansı ki, artıq media axın filtrləri ilə emal edilə bilər. Bizim vəziyyətimizdə biz onları sadəcə qulaqlıqınızın dinamiklərində səsləndirmək üçün səs kartına göndəririk.

İndi proqram təminatının tətbiqinə keçək. Bunun üçün daha əvvəl ayırdığımız qəbuledici və ötürücü faylları birləşdirəcəyik. Bundan əvvəl biz portlar və ünvanlar üçün sabit parametrlərdən istifadə edirdik, lakin indi başlanğıcda təyin etdiyimiz parametrlərdən istifadə etmək üçün proqrama ehtiyacımız var. Bunu etmək üçün biz komanda xətti arqumentlərinin işlənməsi üçün funksionallıq əlavə edərdik. Bundan sonra əlaqə qurmaq istədiyimiz interkomun IP ünvanını və portunu təyin edə biləcəyik.

Əvvəlcə proqrama onun parametrlərini saxlayacaq struktur əlavə edək:

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

typedef struct _app_vars app_vars;

Proqram vars adlı bu tip strukturu elan edəcək.
Sonra, komanda xətti arqumentlərini təhlil etmək üçün funksiya əlavə edək:

/* Функция преобразования аргументов командной строки в
* настройки программы. */
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]);
        }
    }
}

Təhlil nəticəsində komanda xətti arqumentləri vars strukturunun sahələrinə yerləşdiriləcək. Tətbiqin əsas funksiyası filtrlərdən ötürmə və qəbul yollarını toplamaq olacaq; işarəni birləşdirdikdən sonra nəzarət sonsuz bir dövrəyə köçürüləcək, əgər generator tezliyi sıfırdan fərqli olaraq təyin olunarsa, sınaq generatorunu yenidən işə salacaq. dayanmadan işləyir.

Generator dizaynına görə bu yenidən işə salmağa ehtiyac duyacaq; nədənsə 16 saniyədən çox davam edən siqnal çıxara bilmir. Qeyd etmək lazımdır ki, onun müddəti 32 bitlik nömrə ilə müəyyən edilir.

Bütün proqram bu kimi görünəcək:

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

Gəlin tərtib edək. Sonra proqram iki kompüterdə işlədilə bilər. Və ya birində, indi edəcəyim kimi. TShark-ı aşağıdakı arqumentlərlə işə salırıq:

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

Konsoldakı işə salma sahəsində yalnız tutmanın başlaması haqqında bir mesaj göstərilirsə, bu yaxşı bir əlamətdir - bu, portumuzun çox güman ki, digər proqramlar tərəfindən işğal edilmədiyini göstərir. Başqa bir terminalda bu port nömrəsini göstərərək “uzaqdan” interkomu simulyasiya edəcək proqram nümunəsini işə salırıq:

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

Proqram mətnindən göründüyü kimi, standart IP ünvanı 127.0.0.1-dir (yerli geri dönmə).

Başqa bir terminalda biz yerli cihazı simulyasiya edən proqramın ikinci nümunəsini işə salırıq. Daxili test generatorunun işləməsinə imkan verən əlavə bir arqumentdən istifadə edirik:

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

Bu anda "uzaqdan" cihaza ötürülən paketlər TShark ilə konsolda yanıb-sönməyə başlamalıdır və kompüter dinamikindən davamlı bir ton eşidiləcəkdir.

Hər şey yazıldığı kimi baş veribsə, proqramın ikinci nüsxəsini yenidən işə salırıq, lakin açar və arqument olmadan “-gen 440”. İndi generator rolunu oynayacaqsınız. Bundan sonra mikrofona səs-küy yarada bilərsiniz, dinamikdə və ya qulaqlıqda müvafiq səsi eşitməlisiniz. Akustik özünü həyəcanlandırma hətta baş verə bilər; dinamikin səsini azaldın və effekt yox olacaq.

Əgər onu iki kompüterdə işlətmisinizsə və IP ünvanları ilə bağlı çaşqınlıq yaratmamısınızsa, eyni nəticə sizi gözləyir - ikitərəfli rəqəmsal keyfiyyətli səs rabitəsi.

Növbəti məqalədə öz filtrlərimizi - plaginlərimizi necə yazacağımızı öyrənəcəyik, bu bacarıq sayəsində siz media axınından təkcə audio və video üçün deyil, həm də bəzi digər xüsusi sahədə istifadə edə biləcəksiniz.

Mənbə: www.habr.com

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