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 бүтцийн талбарт байрлуулна. Програмын гол үүрэг нь шүүлтүүрээс дамжуулах, хүлээн авах замыг цуглуулах явдал юм; тикерийг холбосны дараа хяналтыг хязгааргүй гогцоонд шилжүүлэх бөгөөд хэрэв генераторын давтамжийг XNUMX биш гэж тохируулсан бол туршилтын генераторыг дахин эхлүүлэх болно. энэ нь зогсолтгүй ажилладаг.

Генератор нь дизайны улмаас эдгээр дахин эхлүүлэх шаардлагатай болно; ямар нэг шалтгааны улмаас 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 (loopback) байна.

Өөр нэг терминал дээр бид локал төхөөрөмжийг дуурайдаг програмын хоёр дахь хувилбарыг ажиллуулдаг. Бид суурилуулсан туршилтын генераторыг ажиллуулах боломжийг олгодог нэмэлт аргументыг ашигладаг:

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

Энэ мөчид "алсын" төхөөрөмж рүү дамжуулсан пакетууд TShark-ийн тусламжтайгаар консол дээр анивчж эхлэх бөгөөд компьютерийн чанга яригчаас тасралтгүй аялгуу сонсогдоно.

Хэрэв бүх зүйл бичсэн шиг болсон бол бид програмын хоёр дахь хуулбарыг дахин эхлүүлнэ, гэхдээ "-gen 440" гэсэн түлхүүр, аргументгүйгээр. Та одоо генераторын дүрд тоглох болно. Үүний дараа та микрофон руу чимээ шуугиан үүсгэж болно, та чанга яригч эсвэл чихэвчний харгалзах дууг сонсох ёстой. Өөрөө акустик өдөөлт ч тохиолдож болно; чанга яригчийн дууг багасгавал эффект алга болно.

Хэрэв та үүнийг хоёр компьютер дээр ажиллуулж, IP хаягийн талаар андуураагүй бол ижил үр дүн таныг хүлээж байна - хоёр талын дижитал чанарын дуут холбоо.

Дараагийн өгүүллээр бид өөрсдийн шүүлтүүр - залгаасуудыг хэрхэн бичих талаар сурах болно, энэ ур чадварын ачаар та медиа дамжуулагчийг зөвхөн аудио, видео төдийгүй бусад тодорхой хэсэгт ашиглах боломжтой болно.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх