ויספאָרשן די Mediastreamer2 וואָיפּ מאָטאָר. טייל 9

דער מאַטעריאַל פון דעם אַרטיקל איז גענומען פון מיין זען קאַנאַל.

דופּלעקס ינטערקאַם

ויספאָרשן די Mediastreamer2 וואָיפּ מאָטאָר. טייל 9

אין דער פאַרגאַנגענהייט אַרטיקל אַ דופּלעקס ינטערקאַם איז מודיע, און אין דעם מיר וועלן מאַכן עס.

די דיאַגראַמע איז געוויזן אין די טיטל פיגור. דער נידעריקער קייט פון פילטערס פארמען די טראַנסמיסיע דרך, וואָס סטאַרץ פון די געזונט קאָרט. עס גיט סיגנאַל סאַמפּאַלז פון די מיקראָפאָן. דורך פעליקייַט, דאָס אַקערז מיט אַ קורס פון 8000 סאַמפּאַלז פּער סעקונדע. די דאַטן ביסל טיפקייַט וואָס מעדיע סטרימער אַודיאָ פילטערס נוצן איז 16 ביץ (דאָס איז נישט וויכטיק; אויב איר ווילט, איר קענען שרייַבן פילטערס וואָס אַרבעט מיט אַ העכער ביסל טיפקייַט). די דאַטן זענען גרופּט אין בלאַקס פון 160 סאַמפּאַלז. אזוי, יעדער בלאָק איז 320 ביטעס אין גרייס. דערנאָך, מיר קאָרמען די דאַטן צו די אַרייַנשרייַב פון די גענעראַטאָר, וואָס, ווען עס איז אויסגעדרייט אַוועק, איז "טראַנספּעראַנט" צו די דאַטן. איך צוגעגעבן עס אין פאַל איר ווערן מיד פון רעדן אין די מיקראָפאָן בעשאַס דיבאַגינג - איר קענען נוצן די גענעראַטאָר צו "דרייען" דעם דרך מיט אַ טאָן סיגנאַל.

נאָך די גענעראַטאָר, דער סיגנאַל גייט צו די ענקאָדער, וואָס קאַנווערץ אונדזער 16-ביסל סאַמפּאַלז לויט די µ-געזעץ (G.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;

דער פּראָגראַם וועט דערקלערן אַ סטרוקטור פון דעם טיפּ גערופן וואַרס.
דערנאָך, לאָזן אונדז לייגן אַ פֿונקציע צו פּאַרסירן אַרגומענטן פון די באַפֿעלן שורה:

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

ווי איר קענען זען פֿון די פּראָגראַם טעקסט, די פעליקייַט IP אַדרעס איז 127.0.0.1 (היגע לופּבאַקק).

אין אן אנדער וואָקזאַל, מיר קאַטער אַ צווייט בייַשפּיל פון דעם פּראָגראַם, וואָס סימיאַלייץ אַ היגע מיטל. מיר נוצן אַן נאָך אַרגומענט וואָס אַלאַוז די געבויט-אין פּרובירן גענעראַטאָר צו אַרבעטן:

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

אין דעם מאָמענט, פּאַקיץ טראַנסמיטטעד צו די "ווייַט" מיטל זאָל אָנהייבן צו בליץ אין די קאַנסאָול מיט TShark, און אַ קעסיידערדיק טאָן וועט זיין געהערט פון די קאָמפּיוטער רעדנער.

אויב אַלץ איז געשען ווי געשריבן, מיר ריסטאַרט די רגע קאָפּיע פון ​​די פּראָגראַם, אָבער אָן די שליסל און אַרגומענט "-gen 440". איר וועט איצט שפּילן די ראָלע פון ​​גענעראַטאָר. נאָך דעם, איר קענען מאַכן ראַש אין די מיקראָפאָן; איר זאָל הערן די קאָראַספּאַנדינג געזונט אין די רעדנער אָדער כעדפאָונז. אַקוסטיש זיך-יקסייטיישאַן קען אפילו פּאַסירן; קער אַראָפּ די רעדנער באַנד און די ווירקונג וועט פאַרשווינדן.

אויב איר לויפן עס אויף צוויי קאָמפּיוטערס און איר האָט נישט צעמישט וועגן די IP אַדרעסעס, דער זעלביקער רעזולטאַט אַווייץ איר - צוויי-וועג דיגיטאַל קוואַליטעט קול קאָמוניקאַציע.

אין דער ווייַטער אַרטיקל מיר וועלן לערנען ווי צו שרייַבן אונדזער אייגענע פילטערס - פּלוגינס, דאַנק צו דעם בקיעס איר קענען נוצן די מידיאַ סטרימער ניט בלויז פֿאַר אַודיאָ און ווידעא, אָבער אויך אין עטלעכע אנדערע ספּעציפיש געגנט.

מקור: www.habr.com

לייגן אַ באַמערקונג