Mediastreamer2 VoIP қозғалтқышын зерттеу. 9 бөлім

Мақала материалы менің сайтымнан алынды zen арнасы.

Дуплексті домофон

Mediastreamer2 VoIP қозғалтқышын зерттеу. 9 бөлім

Өткенде мақала дуплексті домофон жарияланды, біз оны жасаймыз.

Диаграмма тақырыптық суретте көрсетілген. Сүзгілердің төменгі тізбегі дыбыс картасынан басталатын беру жолын құрайды. Ол микрофоннан сигнал үлгілерін береді. Әдепкі бойынша, бұл секундына 8000 үлгі жылдамдығымен орын алады. Медиа ағынды аудио сүзгілері пайдаланатын деректер бит тереңдігі 16 бит (бұл маңызды емес; қаласаңыз, жоғарырақ бит тереңдігімен жұмыс істейтін сүзгілерді жаза аласыз). Деректер 160 үлгіден тұратын блоктарға топтастырылған. Осылайша, әрбір блоктың өлшемі 320 байт. Әрі қарай, біз деректерді генератордың кірісіне береміз, ол өшірілген кезде деректер үшін «мөлдір». Мен оны жөндеу кезінде микрофонмен сөйлесуден шаршаған жағдайда қостым - сіз генераторды дыбыстық сигналмен жолды «ату» үшін пайдалана аласыз.

Генератордан кейін сигнал μ-заңына (G.16 стандарты) сәйкес біздің 711 биттік үлгілерді сегіз разрядтыға түрлендіретін кодерге түседі. Кодер шығысында бізде жарты өлшемді деректер блогы бар. Жалпы, трафикті үнемдеу қажет болмаса, деректерді қысусыз жібере аламыз. Бірақ бұл жерде кодтағышты пайдалану пайдалы, өйткені Wireshark RTP ағынынан дыбысты тек µ-заңына немесе заңға сәйкес қысылғанда ғана шығара алады.

Кодерден кейін деректердің жеңілірек блоктары rtpsend сүзгісіне жіберіледі, ол оларды RTP пакетіне орналастырады, қажетті жалаушаларды орнатады және оларды UDP пакеті түрінде желі арқылы тасымалдау үшін медиа стримерге береді.

Сүзгілердің жоғарғы тізбегі қабылдау жолын құрайды; Желіден медиа ағынмен қабылданатын RTP пакеттері rtprecv сүзгісіне енеді, оның шығысында олар деректер блоктары түрінде пайда болады, олардың әрқайсысы бір қабылданған пакетке сәйкес келеді. Блок тек пайдалы жүктеме деректерін қамтиды, алдыңғы мақалада олар суретте жасыл түспен көрсетілген.

Әрі қарай блоктар олардағы бір байтты үлгілерді сызықтық, 16 биттік үлгілерге түрлендіретін декодер сүзгісіне жіберіледі. Оны мультимедиа ағынды сүзгілері арқылы өңдеуге болады. Біздің жағдайда, біз оларды құлаққаптың динамиктерінде ойнату үшін жай ғана дыбыс картасына жібереміз.

Енді бағдарламалық жасақтаманы енгізуге көшейік. Ол үшін біз бұрын бөлген қабылдағыш және таратқыш файлдарды біріктіреміз. Бұған дейін біз порттар мен мекенжайлар үшін бекітілген параметрлерді пайдаландық, бірақ енді іске қосу кезінде біз көрсеткен параметрлерді пайдалана алу үшін бағдарлама қажет. Ол үшін пәрмен жолы аргументтерін өңдеуге арналған функцияны қосар едік. Осыдан кейін біз байланыс орнатқымыз келетін интеркомның IP мекенжайы мен портын орната аламыз.

Алдымен бағдарламаға оның параметрлерін сақтайтын құрылымды қосамыз:

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

typedef struct _app_vars app_vars;

Бағдарлама осы түрдегі vars деп аталатын құрылымды жариялайды.
Әрі қарай, пәрмен жолы аргументтерін талдау үшін функцияны қосамыз:

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

Талдау нәтижесінде пәрмен жолы дәлелдері vars құрылымының өрістеріне орналастырылады. Қолданбаның негізгі функциясы сүзгілерден жіберу және қабылдау жолдарын жинау болады; тикерді қосқаннан кейін басқару шексіз циклге ауыстырылады, егер генератор жиілігі нөлге тең емес болса, сынақ генераторын қайта іске қосады. ол тоқтаусыз жұмыс істейді.

Генераторға оның дизайнына байланысты бұл қайта қосулар қажет болады; қандай да бір себептермен ол 16 секундтан астам уақытқа созылатын сигнал шығара алмайды. Айта кету керек, оның ұзақтығы 32 биттік санмен көрсетілген.

Бүкіл бағдарлама келесідей болады:

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

Құрастырайық. Содан кейін бағдарламаны екі компьютерде іске қосуға болады. Немесе біреуінде, мен қазір істеймін. Біз TShark бағдарламасын келесі аргументтермен іске қосамыз:

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

Егер консольдегі іске қосу өрісі түсірудің басталуы туралы хабарламаны ғана көрсетсе, бұл жақсы белгі - бұл біздің портты басқа бағдарламалар иеленбегенін білдіреді. Басқа терминалда біз осы порт нөмірін көрсету арқылы «қашықтағы» интеркомды имитациялайтын бағдарлама данасын іске қосамыз:

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

Бағдарлама мәтінінен көрініп тұрғандай, әдепкі IP мекенжайы 127.0.0.1 (жергілікті кері байланыс).

Басқа терминалда біз жергілікті құрылғыны имитациялайтын бағдарламаның екінші данасын іске қосамыз. Біз кірістірілген сынақ генераторының жұмыс істеуіне мүмкіндік беретін қосымша аргумент қолданамыз:

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

Осы сәтте «қашықтағы» құрылғыға жіберілген пакеттер TShark көмегімен консольде жыпылықтай бастайды және компьютер динамигінен үздіксіз дыбыс естіледі.

Егер бәрі жазылғандай болса, біз бағдарламаның екінші көшірмесін қайта іске қосамыз, бірақ кілт пен аргументсіз «-gen 440». Сіз енді генератор рөлін ойнайсыз. Осыдан кейін микрофонға шу шығаруға болады, динамиктен немесе құлақаспаптан сәйкес дыбысты есту керек. Тіпті акустикалық өздігінен қозу пайда болуы мүмкін; динамиктің дыбыс деңгейін төмендетіңіз, сонда әсер жоғалады.

Егер сіз оны екі компьютерде іске қоссаңыз және IP мекенжайлары туралы шатастырмасаңыз, сізді дәл сол нәтиже күтеді - екі жақты сандық сапалы дауыстық байланыс.

Келесі мақалада біз өз сүзгілерімізді - плагиндерді қалай жазу керектігін үйренеміз, осы дағдының арқасында сіз медиа ағынды тек аудио және бейне үшін ғана емес, сонымен қатар басқа белгілі бір салада да пайдалана аласыз.

Ақпарат көзі: www.habr.com

пікір қалдыру