Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 9

Materiali i artikullit është marrë nga ime kanal zen.

Intercom dupleks

Eksplorimi i motorit VoIP Mediastreamer2. Pjesa 9

Në të fundit artikull u njoftua një intercom dupleks dhe në këtë do ta bëjmë.

Diagrami është paraqitur në figurën e titullit. Zinxhiri i poshtëm i filtrave formon rrugën e transmetimit, e cila fillon nga karta e zërit. Ofron mostra sinjalesh nga mikrofoni. Si parazgjedhje, kjo ndodh me një shpejtësi prej 8000 mostrash në sekondë. Thellësia e bitit të të dhënave që përdorin filtrat audio të transmetuesit të medias është 16 bit (kjo nuk është e rëndësishme; nëse dëshironi, mund të shkruani filtra që do të funksionojnë me një thellësi bit më të lartë). Të dhënat grupohen në blloqe prej 160 mostrash. Kështu, çdo bllok është 320 bajt në madhësi. Më pas, ne i ushqejmë të dhënat në hyrjen e gjeneratorit, i cili, kur fiket, është "transparent" ndaj të dhënave. E shtova në rast se lodheni duke folur në mikrofon gjatë korrigjimit - mund të përdorni gjeneratorin për të "xhiruar" shtegun me një sinjal toni.

Pas gjeneratorit, sinjali shkon te koduesi, i cili konverton mostrat tona 16-bitësh sipas ligjit μ (standard G.711) në ato tetë-bitësh. Në daljen e koduesit, ne tashmë kemi një bllok të dhënash sa gjysma e madhësisë. Në përgjithësi, ne mund të transmetojmë të dhëna pa kompresim nëse nuk kemi nevojë të kursejmë trafik. Por këtu është e dobishme të përdorni një kodues, pasi Wireshark mund të riprodhojë audio nga një transmetim RTP vetëm kur është i ngjeshur sipas ligjit µ ose a-ligjit.

Pas koduesit, blloqet më të lehta të të dhënave dërgohen në filtrin rtpsend, i cili do t'i vendosë në një paketë RTP, do të vendosë flamujt e nevojshëm dhe do t'i japë ato transmetuesit të medias për transmetim në rrjet në formën e një pakete UDP.

Zinxhiri i sipërm i filtrave formon shtegun e marrjes; paketat RTP të marra nga transmetuesi i medias nga rrjeti hyjnë në filtrin rtprecv, në daljen e të cilit shfaqen në formën e blloqeve të të dhënave, secila prej të cilave korrespondon me një paketë të marrë. Blloku përmban vetëm të dhëna të ngarkesës; në artikullin e mëparshëm ato u shfaqën me ngjyrë të gjelbër në ilustrim.

Më pas, blloqet dërgohen në filtrin e dekoderit, i cili konverton mostrat me një bajt që gjenden në to në ato lineare, 16-bit. Të cilat tashmë mund të përpunohen nga filtrat e transmetuesit të medias. Në rastin tonë, ne thjesht i dërgojmë ato në kartën e zërit për t'u riprodhuar në altoparlantët e kufjeve tuaja.

Tani le të kalojmë në zbatimin e softuerit. Për ta bërë këtë, ne do të kombinojmë skedarët marrës dhe transmetues që kemi ndarë më parë. Para kësaj, ne përdornim cilësime fikse për portet dhe adresat, por tani na duhet që programi të jetë në gjendje të përdorë cilësimet që specifikojmë në fillim. Për ta bërë këtë, ne do të shtonim funksionalitetin për përpunimin e argumenteve të linjës së komandës. Pas së cilës do të mund të vendosim adresën IP dhe portin e interkomit me të cilin duam të krijojmë një lidhje.

Së pari, le të shtojmë një strukturë në program që do të ruajë cilësimet e tij:

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

typedef struct _app_vars app_vars;

Programi do të deklarojë një strukturë të këtij lloji të quajtur vars.
Më pas, le të shtojmë një funksion për të analizuar argumentet e linjës së komandës:

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

Si rezultat i analizimit, argumentet e linjës komanduese do të vendosen në fushat e strukturës vars. Funksioni kryesor i aplikacionit do të jetë mbledhja e shtigjeve të transmetimit dhe marrjes nga filtrat; pas lidhjes së shenjës, kontrolli do të transferohet në një lak të pafund i cili, nëse frekuenca e gjeneratorit është vendosur në jo zero, do të rindizet gjeneratorin e provës në mënyrë që funksionon pa u ndalur.

Gjeneratorit do t'i duhen këto rinisje për shkak të dizajnit të tij; për disa arsye ai nuk mund të prodhojë një sinjal që zgjat më shumë se 16 sekonda. Duhet të theksohet se kohëzgjatja e tij përcaktohet nga një numër 32-bit.

I gjithë programi do të duket kështu:

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

Le të përpilojmë. Pastaj programi mund të ekzekutohet në dy kompjuterë. Ose në një, siç do të bëj tani. Ne nisim TShark me argumentet e mëposhtme:

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

Nëse fusha e nisjes në tastierë shfaq vetëm një mesazh për fillimin e kapjes, atëherë kjo është një shenjë e mirë - do të thotë që porti ynë ka shumë të ngjarë të mos jetë i zënë nga programe të tjera. Në një terminal tjetër, ne lëshojmë një shembull programi që do të simulojë një intercom "në distancë" duke specifikuar këtë numër porti:

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

Siç mund të shihet nga teksti i programit, adresa IP e parazgjedhur është 127.0.0.1 (loopback lokal).

Në një terminal tjetër, ne lëshojmë një shembull të dytë të programit, i cili simulon një pajisje lokale. Ne përdorim një argument shtesë që lejon gjeneratorin e integruar të testit të funksionojë:

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

Në këtë moment, paketat e transmetuara drejt pajisjes "në distancë" duhet të fillojnë të ndezin në tastierë me TShark dhe një ton i vazhdueshëm do të dëgjohet nga altoparlanti i kompjuterit.

Nëse gjithçka ka ndodhur siç është shkruar, atëherë ne rifillojmë kopjen e dytë të programit, por pa çelësin dhe argumentin "-gen 440". Tani do të luani rolin e gjeneratorit. Pas kësaj, mund të bëni zhurmë në mikrofon; duhet të dëgjoni tingullin përkatës në altoparlant ose kufje. Madje mund të ndodhë edhe vetë-ngacmim akustik; ulni volumin e altoparlantit dhe efekti do të zhduket.

Nëse e keni ekzekutuar në dy kompjuterë dhe nuk jeni ngatërruar për adresat IP, atëherë ju pret i njëjti rezultat - komunikim zanor me cilësi dixhitale të dyanshme.

Në artikullin tjetër do të mësojmë se si të shkruajmë filtrat tanë - shtojcat, falë kësaj aftësie do të mund të përdorni transmetuesin e medias jo vetëm për audio dhe video, por edhe në ndonjë fushë tjetër specifike.

Burimi: www.habr.com

Shto një koment