Mediastreamer2 वीओआईपी इंजन की खोज। भाग ---- पहला

लेख की सामग्री मेरे से ली गई है ज़ेन चैनल.

डुप्लेक्स इंटरकॉम

Mediastreamer2 वीओआईपी इंजन की खोज। भाग ---- पहला

पिछले लेख एक डुप्लेक्स इंटरकॉम की घोषणा की गई थी, और हम इसे इसमें बनाएंगे।

आरेख शीर्षक चित्र में दिखाया गया है। फ़िल्टर की निचली श्रृंखला ट्रांसमिशन पथ बनाती है, जो साउंड कार्ड से शुरू होती है। यह माइक्रोफ़ोन से सिग्नल नमूने प्रदान करता है। डिफ़ॉल्ट रूप से, यह प्रति सेकंड 8000 नमूनों की दर से होता है। मीडिया स्ट्रीमर ऑडियो फ़िल्टर द्वारा उपयोग की जाने वाली डेटा बिट गहराई 16 बिट है (यह महत्वपूर्ण नहीं है; यदि आप चाहें, तो आप फ़िल्टर लिख सकते हैं जो उच्च बिट गहराई के साथ काम करेंगे)। डेटा को 160 नमूनों के ब्लॉक में समूहीकृत किया गया है। इस प्रकार, प्रत्येक ब्लॉक का आकार 320 बाइट्स है। इसके बाद, हम डेटा को जनरेटर के इनपुट में फीड करते हैं, जो बंद होने पर, डेटा के लिए "पारदर्शी" होता है। यदि आप डिबगिंग के दौरान माइक्रोफ़ोन में बात करते-करते थक जाते हैं तो मैंने इसे जोड़ा है - आप टोन सिग्नल के साथ पथ को "शूट" करने के लिए जनरेटर का उपयोग कर सकते हैं।

जनरेटर के बाद, सिग्नल एनकोडर को जाता है, जो हमारे 16-बिट नमूनों को μ-लॉ (जी.711 मानक) के अनुसार आठ-बिट वाले में परिवर्तित करता है। एनकोडर के आउटपुट पर, हमारे पास पहले से ही आधे आकार का डेटा ब्लॉक है। सामान्य तौर पर, यदि हमें ट्रैफ़िक बचाने की आवश्यकता नहीं है तो हम बिना संपीड़न के डेटा संचारित कर सकते हैं। लेकिन यहां एनकोडर का उपयोग करना उपयोगी है, क्योंकि वायरशार्क आरटीपी स्ट्रीम से ऑडियो तभी पुन: उत्पन्न कर सकता है जब इसे µ-लॉ या ए-लॉ के अनुसार संपीड़ित किया जाता है।

एनकोडर के बाद, डेटा के हल्के ब्लॉक को आरटीपीएसेंड फिल्टर में भेजा जाता है, जो उन्हें आरटीपी पैकेट में रखेगा, आवश्यक झंडे सेट करेगा और उन्हें यूडीपी पैकेट के रूप में नेटवर्क पर ट्रांसमिशन के लिए मीडिया स्ट्रीमर को देगा।

फ़िल्टर की ऊपरी श्रृंखला प्राप्त पथ बनाती है; नेटवर्क से मीडिया स्ट्रीमर द्वारा प्राप्त आरटीपी पैकेट rtprecv फ़िल्टर में प्रवेश करते हैं, जिसके आउटपुट पर वे डेटा ब्लॉक के रूप में दिखाई देते हैं, जिनमें से प्रत्येक एक प्राप्त पैकेट से मेल खाता है। ब्लॉक में केवल पेलोड डेटा है; पिछले लेख में उन्हें चित्रण में हरे रंग में दिखाया गया था।

इसके बाद, ब्लॉकों को डिकोडर फ़िल्टर में भेजा जाता है, जो उनमें मौजूद एकल-बाइट नमूनों को रैखिक, 16-बिट वाले में परिवर्तित करता है। जिसे पहले से ही मीडिया स्ट्रीमर फ़िल्टर द्वारा संसाधित किया जा सकता है। हमारे मामले में, हम बस उन्हें आपके हेडसेट के स्पीकर पर प्लेबैक के लिए साउंड कार्ड में भेजते हैं।

अब सॉफ्टवेयर कार्यान्वयन की ओर बढ़ते हैं। ऐसा करने के लिए, हम रिसीवर और ट्रांसमीटर फ़ाइलों को संयोजित करेंगे जिन्हें हमने पहले अलग किया था। इससे पहले, हम पोर्ट और पते के लिए निश्चित सेटिंग्स का उपयोग करते थे, लेकिन अब हमें प्रोग्राम की आवश्यकता है कि वह उन सेटिंग्स का उपयोग करने में सक्षम हो जो हम स्टार्टअप पर निर्दिष्ट करते हैं। ऐसा करने के लिए, हम कमांड लाइन तर्कों को संसाधित करने के लिए कार्यक्षमता जोड़ेंगे। जिसके बाद हम इंटरकॉम का आईपी एड्रेस और पोर्ट सेट कर पाएंगे जिसके साथ हम कनेक्शन स्थापित करना चाहते हैं।

सबसे पहले, आइए प्रोग्राम में एक संरचना जोड़ें जो इसकी सेटिंग्स को संग्रहीत करेगी:

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

typedef struct _app_vars app_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]);
        }
    }
}

पार्सिंग के परिणामस्वरूप, कमांड लाइन तर्कों को वर्र्स संरचना के क्षेत्रों में रखा जाएगा। एप्लिकेशन का मुख्य कार्य फ़िल्टर से ट्रांसमिटिंग और प्राप्त पथ एकत्र करना होगा; टिकर को कनेक्ट करने के बाद, नियंत्रण को एक अनंत लूप में स्थानांतरित किया जाएगा, जो यदि जनरेटर आवृत्ति को गैर-शून्य पर सेट किया गया था, तो परीक्षण जनरेटर को पुनरारंभ करेगा ताकि यह बिना रुके काम करता है.

जनरेटर को इसके डिज़ाइन के कारण इन पुनरारंभ की आवश्यकता होगी; किसी कारण से यह 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

जैसा कि प्रोग्राम टेक्स्ट से देखा जा सकता है, डिफ़ॉल्ट आईपी पता 127.0.0.1 (स्थानीय लूपबैक) है।

दूसरे टर्मिनल में, हम प्रोग्राम का दूसरा उदाहरण लॉन्च करते हैं, जो एक स्थानीय डिवाइस का अनुकरण करता है। हम एक अतिरिक्त तर्क का उपयोग करते हैं जो अंतर्निहित परीक्षण जनरेटर को काम करने की अनुमति देता है:

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

इस समय, "रिमोट" डिवाइस की ओर प्रेषित पैकेट टीशार्क के साथ कंसोल में फ्लैश होना शुरू हो जाना चाहिए, और कंप्यूटर स्पीकर से एक निरंतर टोन सुनाई देगी।

यदि सब कुछ लिखित रूप में हुआ, तो हम प्रोग्राम की दूसरी प्रति को पुनरारंभ करते हैं, लेकिन कुंजी और तर्क के बिना "-जेन 440"। अब आप जनरेटर की भूमिका निभाएंगे। इसके बाद, आप माइक्रोफ़ोन में शोर कर सकते हैं; आपको स्पीकर या हेडफ़ोन में संबंधित ध्वनि सुननी चाहिए। ध्वनिक आत्म-उत्तेजना भी हो सकती है; स्पीकर की आवाज़ कम कर दें और प्रभाव गायब हो जाएगा।

यदि आपने इसे दो कंप्यूटरों पर चलाया और आईपी पते के बारे में भ्रमित नहीं हुए, तो वही परिणाम आपका इंतजार कर रहा है - दो-तरफ़ा डिजिटल गुणवत्ता वाला ध्वनि संचार।

अगले लेख में हम सीखेंगे कि अपने स्वयं के फ़िल्टर - प्लगइन्स कैसे लिखें, इस कौशल के लिए धन्यवाद आप न केवल ऑडियो और वीडियो के लिए, बल्कि किसी अन्य विशिष्ट क्षेत्र में भी मीडिया स्ट्रीमर का उपयोग करने में सक्षम होंगे।

स्रोत: www.habr.com

एक टिप्पणी जोड़ें