Khám phá công cụ VoIP Mediastreamer2. Phần 9

Tài liệu của bài viết được lấy từ tài liệu của tôi kênh thiền.

liên lạc nội bộ song công

Khám phá công cụ VoIP Mediastreamer2. Phần 9

Cuối cùng Bài viết một hệ thống liên lạc nội bộ song công đã được công bố và chúng tôi sẽ thực hiện nó trong hệ thống này.

Sơ đồ được thể hiện trong hình tiêu đề. Chuỗi bộ lọc phía dưới tạo thành đường truyền, bắt đầu từ card âm thanh. Nó cung cấp các mẫu tín hiệu từ micro. Theo mặc định, điều này xảy ra với tốc độ 8000 mẫu mỗi giây. Độ sâu bit dữ liệu mà bộ lọc âm thanh của bộ truyền phát phương tiện sử dụng là 16 bit (điều này không quan trọng; nếu muốn, bạn có thể viết các bộ lọc sẽ hoạt động với độ sâu bit cao hơn). Dữ liệu được nhóm thành các khối gồm 160 mẫu. Như vậy, mỗi khối có kích thước 320 byte. Tiếp theo, chúng tôi cung cấp dữ liệu cho đầu vào của trình tạo, khi tắt, dữ liệu này sẽ “trong suốt” đối với dữ liệu. Tôi đã thêm tính năng này trong trường hợp bạn cảm thấy mệt mỏi khi nói vào micrô trong khi gỡ lỗi - bạn có thể sử dụng trình tạo để “bắn” đường dẫn bằng tín hiệu âm thanh.

Sau bộ tạo, tín hiệu đi đến bộ mã hóa, bộ mã hóa này chuyển đổi các mẫu 16 bit của chúng tôi theo luật µ (tiêu chuẩn G.711) thành mẫu XNUMX bit. Ở đầu ra của bộ mã hóa, chúng ta đã có một khối dữ liệu có kích thước bằng một nửa. Nói chung, chúng ta có thể truyền dữ liệu mà không cần nén nếu không cần tiết kiệm lưu lượng. Nhưng ở đây, việc sử dụng bộ mã hóa sẽ rất hữu ích vì Wireshark chỉ có thể tái tạo âm thanh từ luồng RTP khi nó được nén theo luật µ hoặc luật a.

Sau bộ mã hóa, các khối dữ liệu nhẹ hơn sẽ được gửi đến bộ lọc rtpsend, bộ lọc này sẽ đặt chúng vào gói RTP, đặt các cờ cần thiết và đưa chúng đến bộ truyền phát phương tiện để truyền qua mạng dưới dạng gói UDP.

Chuỗi bộ lọc phía trên tạo thành đường dẫn nhận; Các gói RTP mà bộ truyền phát phương tiện nhận được từ mạng đi vào bộ lọc rtprecv, ở đầu ra chúng xuất hiện dưới dạng khối dữ liệu, mỗi gói tương ứng với một gói nhận được. Khối chỉ chứa dữ liệu tải trọng; trong bài viết trước chúng được hiển thị bằng màu xanh lục trong hình minh họa.

Tiếp theo, các khối được gửi đến bộ lọc giải mã, bộ lọc này sẽ chuyển đổi các mẫu byte đơn chứa trong chúng thành các mẫu tuyến tính 16 bit. Điều này đã có thể được xử lý bởi các bộ lọc truyền phát phương tiện. Trong trường hợp của chúng tôi, chúng tôi chỉ cần gửi chúng tới card âm thanh để phát lại trên loa tai nghe của bạn.

Bây giờ hãy chuyển sang triển khai phần mềm. Để làm điều này, chúng tôi sẽ kết hợp các tệp máy thu và máy phát mà chúng tôi đã tách trước đó. Trước đó, chúng tôi đã sử dụng các cài đặt cố định cho cổng và địa chỉ, nhưng bây giờ chúng tôi cần chương trình có thể sử dụng các cài đặt mà chúng tôi chỉ định khi khởi động. Để làm điều này, chúng tôi sẽ thêm chức năng xử lý các đối số dòng lệnh. Sau đó, chúng tôi sẽ có thể đặt địa chỉ IP và cổng của hệ thống liên lạc nội bộ mà chúng tôi muốn thiết lập kết nối.

Trước tiên, hãy thêm cấu trúc vào chương trình sẽ lưu trữ các cài đặt của nó:

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

typedef struct _app_vars app_vars;

Chương trình sẽ khai báo một cấu trúc kiểu này gọi là vars.
Tiếp theo, hãy thêm một hàm để phân tích các đối số dòng lệnh:

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

Kết quả của việc phân tích cú pháp, các đối số dòng lệnh sẽ được đặt trong các trường của cấu trúc vars. Chức năng chính của ứng dụng sẽ là thu thập các đường truyền và nhận từ các bộ lọc; sau khi kết nối mã đánh dấu, điều khiển sẽ được chuyển sang một vòng lặp vô hạn, nếu tần số máy phát được đặt thành khác 0, sẽ khởi động lại máy phát thử nghiệm để nó hoạt động mà không dừng lại.

Máy phát điện sẽ cần những lần khởi động lại này do thiết kế của nó; vì lý do nào đó, nó không thể tạo ra tín hiệu kéo dài hơn 16 giây. Cần lưu ý rằng thời lượng của nó được chỉ định bởi số 32 bit.

Toàn bộ chương trình sẽ trông như thế này:

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

Hãy biên dịch. Sau đó chương trình có thể chạy trên hai máy tính. Hoặc trên một, như tôi sẽ làm bây giờ. Chúng tôi khởi chạy TShark với các đối số sau:

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

Nếu trường khởi chạy trong bảng điều khiển chỉ hiển thị thông báo về việc bắt đầu chụp thì đây là một dấu hiệu tốt - điều đó có nghĩa là cổng của chúng tôi rất có thể không bị các chương trình khác chiếm giữ. Trong một thiết bị đầu cuối khác, chúng tôi khởi chạy một phiên bản chương trình sẽ mô phỏng hệ thống liên lạc nội bộ “từ xa” bằng cách chỉ định số cổng này:

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

Như có thể thấy từ văn bản chương trình, địa chỉ IP mặc định là 127.0.0.1 (loopback cục bộ).

Trong một thiết bị đầu cuối khác, chúng tôi khởi chạy phiên bản thứ hai của chương trình, phiên bản này mô phỏng một thiết bị cục bộ. Chúng tôi sử dụng một đối số bổ sung cho phép trình tạo thử nghiệm tích hợp hoạt động:

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

Tại thời điểm này, các gói được truyền tới thiết bị “từ xa” sẽ bắt đầu nhấp nháy trong bảng điều khiển bằng TShark và bạn sẽ nghe thấy âm báo liên tục từ loa máy tính.

Nếu mọi thứ diễn ra như đã viết thì chúng ta khởi động lại bản sao thứ hai của chương trình nhưng không có khóa và đối số “—gen 440”. Bây giờ bạn sẽ đóng vai trò là người tạo ra. Sau đó, bạn có thể tạo tiếng ồn vào micrô, bạn sẽ nghe thấy âm thanh tương ứng trong loa hoặc tai nghe. Thậm chí có thể xảy ra hiện tượng tự kích thích âm thanh; hãy giảm âm lượng loa xuống và hiệu ứng sẽ biến mất.

Nếu bạn chạy nó trên hai máy tính và không bị nhầm lẫn về địa chỉ IP, thì kết quả tương tự đang chờ bạn - giao tiếp thoại chất lượng kỹ thuật số hai chiều.

Trong bài viết tiếp theo, chúng ta sẽ tìm hiểu cách viết các bộ lọc - plugin của riêng mình, nhờ kỹ năng này, bạn sẽ có thể sử dụng trình phát đa phương tiện không chỉ cho âm thanh và video mà còn trong một số lĩnh vực cụ thể khác.

Nguồn: www.habr.com

Thêm một lời nhận xét