Verken die Mediastreamer2 VoIP-enjin. Deel 9

Die materiaal van die artikel is geneem uit my zen-kanaal.

Dupleks interkom

Verken die Mediastreamer2 VoIP-enjin. Deel 9

In die laaste Artikel 'n dupleks interkom is aangekondig, en in hierdie een sal ons dit maak.

Die diagram word in die titelfiguur getoon. Die onderste ketting filters vorm die transmissiepad, wat vanaf die klankkaart begin. Dit verskaf seinmonsters vanaf die mikrofoon. By verstek vind dit plaas teen 'n tempo van 8000 monsters per sekonde. Die databisdiepte wat mediastreamer-oudiofilters gebruik, is 16 bisse (dit is nie belangrik nie; as jy wil, kan jy filters skryf wat met 'n hoër bisdiepte sal werk). Die data is gegroepeer in blokke van 160 monsters. Dus, elke blok is 320 grepe groot. Vervolgens voer ons die data na die insette van die kragopwekker, wat, wanneer dit afgeskakel is, "deursigtig" vir die data is. Ek het dit bygevoeg vir ingeval jy moeg word om in die mikrofoon te praat tydens ontfouting - jy kan die kragopwekker gebruik om die pad met 'n toonsein te "skiet".

Na die kragopwekker gaan die sein na die enkodeerder, wat ons 16-bis monsters omskakel volgens die µ-wet (G.711 standaard) in agt-bis ene. By die uitset van die enkodeerder het ons reeds 'n datablok wat die helfte van die grootte is. Oor die algemeen kan ons data sonder kompressie oordra as ons nie verkeer hoef te bespaar nie. Maar hier is dit nuttig om 'n enkodeerder te gebruik, aangesien Wireshark klank vanaf 'n RTP-stroom kan reproduseer slegs wanneer dit volgens die µ-wet of a-wet saamgepers is.

Na die enkodeerder word die ligter blokke data na die rtpsend-filter gestuur, wat hulle in 'n RTP-pakkie sal plaas, die nodige vlae sal stel en dit aan die mediastreamer gee vir oordrag oor die netwerk in die vorm van 'n UDP-pakkie.

Die boonste ketting van filters vorm die ontvangspad; RTP-pakkies wat deur die mediastreamer van die netwerk ontvang word, gaan die rtprecv-filter in, by die uitset waarvan hulle verskyn in die vorm van datablokke, wat elk ooreenstem met een ontvangde pakkie. Die blok bevat slegs loonvragdata; in die vorige artikel is dit in groen in die illustrasie getoon.

Vervolgens word die blokke na die dekodeerderfilter gestuur, wat die enkelgreep-monsters wat daarin vervat is, omskakel na lineêre, 16-bis. Wat reeds deur mediastreamer-filters verwerk kan word. In ons geval stuur ons dit eenvoudig na die klankkaart vir afspeel op die luidsprekers van jou headset.

Kom ons gaan nou oor na sagteware-implementering. Om dit te doen, sal ons die ontvanger- en senderlêers wat ons voorheen geskei het, kombineer. Voor dit het ons vaste instellings vir poorte en adresse gebruik, maar nou het ons die program nodig om die instellings te kan gebruik wat ons by opstart spesifiseer. Om dit te doen, sal ons funksionaliteit byvoeg vir die verwerking van opdragreëlargumente. Daarna sal ons die IP-adres en poort van die interkom kan stel waarmee ons 'n verbinding wil vestig.

Kom ons voeg eers 'n struktuur by die program wat sy instellings sal stoor:

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

typedef struct _app_vars app_vars;

Die program sal 'n struktuur van hierdie tipe genaamd vars verklaar.
Kom ons voeg dan 'n funksie by om opdragreëlargumente te ontleed:

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

As gevolg van ontleding sal die opdragreëlargumente in die velde van die vars-struktuur geplaas word. Die hooffunksie van die toepassing sal wees om versending- en ontvangpaaie van filters te versamel; nadat die tikker gekoppel is, sal beheer oorgedra word na 'n oneindige lus wat, indien die generatorfrekwensie op nie-nul gestel is, die toetsgenerator sal herbegin sodat dit werk sonder ophou.

Die kragopwekker sal hierdie herbegin nodig hê as gevolg van sy ontwerp; om een ​​of ander rede kan dit nie 'n sein produseer wat langer as 16 sekondes duur nie. Daar moet kennis geneem word dat die duur daarvan gespesifiseer word deur 'n 32-bis nommer.

Die hele program sal so lyk:

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

Kom ons stel saam. Dan kan die program op twee rekenaars uitgevoer word. Of op een, soos ek nou sal doen. Ons begin TShark met die volgende argumente:

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

As die bekendstellingsveld in die konsole slegs 'n boodskap oor die begin van opname vertoon, is dit 'n goeie teken - dit beteken dat ons poort heel waarskynlik nie deur ander programme beset word nie. In 'n ander terminale begin ons 'n programinstansie wat 'n "afgeleë" interkom sal simuleer deur hierdie poortnommer te spesifiseer:

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

Soos uit die programteks gesien kan word, is die verstek IP-adres 127.0.0.1 (plaaslike teruglus).

In 'n ander terminale begin ons 'n tweede instansie van die program, wat 'n plaaslike toestel simuleer. Ons gebruik 'n bykomende argument wat die ingeboude toetsgenerator toelaat om te werk:

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

Op hierdie oomblik moet pakkies wat na die "afgeleë" toestel gestuur word, in die konsole met TShark begin flits, en 'n deurlopende toon sal van die rekenaarluidspreker gehoor word.

As alles gebeur het soos geskryf, dan herbegin ons die tweede kopie van die program, maar sonder die sleutel en argument "—gen 440". Jy sal nou die rol van kragopwekker speel. Hierna kan jy geraas in die mikrofoon maak; jy behoort die ooreenstemmende klank in die luidspreker of oorfone te hoor. Akoestiese selfopwekking kan selfs voorkom; draai die luidsprekervolume af en die effek sal verdwyn.

As jy dit op twee rekenaars laat loop het en nie verward geraak het oor die IP-adresse nie, dan wag dieselfde resultaat op jou - tweerigting digitale kwaliteit stemkommunikasie.

In die volgende artikel sal ons leer hoe om ons eie filters te skryf - plugins, danksy hierdie vaardigheid sal u die mediastreamer nie net vir klank en video kan gebruik nie, maar ook in 'n ander spesifieke area.

Bron: will.com

Voeg 'n opmerking