Mediastreamer2 VoIP ఇంజిన్‌ను అన్వేషిస్తోంది. 9 వ భాగము

వ్యాసం యొక్క పదార్థం నా నుండి తీసుకోబడింది జెన్ ఛానల్.

డ్యూప్లెక్స్ ఇంటర్‌కామ్

Mediastreamer2 VoIP ఇంజిన్‌ను అన్వేషిస్తోంది. 9 వ భాగము

గతం లో వ్యాసం డ్యూప్లెక్స్ ఇంటర్‌కామ్ ప్రకటించబడింది మరియు ఇందులో మేము దీన్ని చేస్తాము.

రేఖాచిత్రం శీర్షిక చిత్రంలో చూపబడింది. ఫిల్టర్‌ల దిగువ గొలుసు ప్రసార మార్గాన్ని ఏర్పరుస్తుంది, ఇది సౌండ్ కార్డ్ నుండి ప్రారంభమవుతుంది. ఇది మైక్రోఫోన్ నుండి సిగ్నల్ నమూనాలను అందిస్తుంది. డిఫాల్ట్‌గా, ఇది సెకనుకు 8000 నమూనాల చొప్పున జరుగుతుంది. మీడియా స్ట్రీమర్ ఆడియో ఫిల్టర్‌లు ఉపయోగించే డేటా బిట్ డెప్త్ 16 బిట్‌లు (ఇది ముఖ్యం కాదు; మీరు కోరుకుంటే, మీరు ఎక్కువ బిట్ డెప్త్‌తో పని చేసే ఫిల్టర్‌లను వ్రాయవచ్చు). డేటా 160 నమూనాల బ్లాక్‌లుగా వర్గీకరించబడింది. అందువలన, ప్రతి బ్లాక్ పరిమాణం 320 బైట్లు. తరువాత, మేము జెనరేటర్ యొక్క ఇన్‌పుట్‌కు డేటాను ఫీడ్ చేస్తాము, ఇది ఆపివేయబడినప్పుడు, డేటాకు "పారదర్శకంగా" ఉంటుంది. డీబగ్గింగ్ సమయంలో మీరు మైక్రోఫోన్‌లో మాట్లాడటం అలసిపోతే నేను దానిని జోడించాను - మీరు టోన్ సిగ్నల్‌తో మార్గాన్ని "షూట్" చేయడానికి జనరేటర్‌ని ఉపయోగించవచ్చు.

జనరేటర్ తర్వాత, సిగ్నల్ ఎన్‌కోడర్‌కి వెళుతుంది, ఇది మా 16-బిట్ నమూనాలను µ-లా (G.711 ప్రమాణం) ప్రకారం ఎనిమిది-బిట్‌లుగా మారుస్తుంది. ఎన్‌కోడర్ అవుట్‌పుట్ వద్ద, మేము ఇప్పటికే సగం పరిమాణంలో డేటా బ్లాక్‌ని కలిగి ఉన్నాము. సాధారణంగా, మనం ట్రాఫిక్‌ను ఆదా చేయనవసరం లేకపోతే కుదింపు లేకుండా డేటాను ప్రసారం చేయవచ్చు. కానీ ఇక్కడ ఎన్‌కోడర్‌ను ఉపయోగించడం ఉపయోగకరంగా ఉంటుంది, ఎందుకంటే Wireshark µ-law లేదా a-law ప్రకారం కంప్రెస్ చేయబడినప్పుడు మాత్రమే 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

ఒక వ్యాఖ్యను జోడించండి