Explorando el motor VoIP de Mediastreamer2. Parte 2

El material del artículo está tomado de mi canal zen.

Explorando el motor VoIP de Mediastreamer2. Parte 2

Crear un generador de tonos

En el anterior статье Instalamos la biblioteca de transmisión de medios y las herramientas de desarrollo y probamos su funcionalidad mediante la creación de una aplicación de prueba.

Hoy crearemos una aplicación que puede generar una señal de tono en una tarjeta de sonido. Para resolver este problema necesitamos conectar los filtros al circuito generador de sonido que se muestra a continuación:

Explorando el motor VoIP de Mediastreamer2. Parte 2

Leemos el diagrama de izquierda a derecha, esta es la dirección en la que se mueve nuestro flujo de datos. Las flechas también lo indican. Los rectángulos indican filtros que procesan bloques de datos y generan el resultado. Dentro del rectángulo, se indica su función y el tipo de filtro se indica en letras mayúsculas justo debajo. Las flechas que conectan los rectángulos son colas de datos a través de las cuales se entregan bloques de datos de un filtro a otro. En general, un filtro puede tener muchas entradas y salidas.

Todo comienza con la fuente del reloj, que establece el tempo al que se calculan los datos en los filtros. Según su ciclo de reloj, cada filtro procesa todos los bloques de datos que se encuentran en su entrada. Y pone bloques con el resultado en la cola. Primero, el filtro más cercano a la fuente del reloj realiza los cálculos, luego los filtros conectados a sus salidas (puede haber muchas salidas), y así sucesivamente. Una vez que el último filtro de la cadena termina de procesarse, la ejecución se detiene hasta que llega un nuevo reloj. Los latidos, por defecto, siguen un intervalo de 10 milisegundos.

Volvamos a nuestro diagrama. Los ciclos de reloj llegan a la entrada de la fuente de silencio; este es un filtro, que está ocupado generando un bloque de datos que contiene ceros en su salida para cada ciclo de reloj. Si consideramos este bloque como un bloque de muestras de sonido, entonces esto no es más que silencio. A primera vista, parece extraño generar bloques de datos en silencio; después de todo, no se pueden escuchar, pero estos bloques son necesarios para el funcionamiento del generador de señales de sonido. El generador utiliza estos bloques como una hoja de papel en blanco y graba muestras de sonido en ellos. En su estado normal, el generador se apaga y simplemente reenvía los bloques de entrada a la salida. Así, bloques de silencio recorren sin cambios todo el circuito de izquierda a derecha, acabando en la tarjeta de sonido. Que silenciosamente toma bloques de la cola conectada a su entrada.

Pero todo cambia si al generador se le da la orden de reproducir sonido, comienza a generar muestras de sonido y las reemplaza con muestras en los bloques de entrada y coloca los bloques modificados en la salida. La tarjeta de sonido comienza a reproducir sonido. A continuación se muestra un programa que implementa el esquema de trabajo descrito anteriormente:

/* Файл mstest2.c */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
int main()
{
    ms_init();

    /* Создаем экземпляры фильтров. */
    MSFilter  *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
    MSFilter  *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
    MSSndCard *card_playback = ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
    MSFilter  *snd_card_write = ms_snd_card_create_writer(card_playback);

    /* Создаем тикер. */
    MSTicker *ticker = ms_ticker_new();

    /* Соединяем фильтры в цепочку. */
    ms_filter_link(voidsource, 0, dtmfgen, 0);
    ms_filter_link(dtmfgen, 0, snd_card_write, 0);

   /* Подключаем источник тактов. */
   ms_ticker_attach(ticker, voidsource);

   /* Включаем звуковой генератор. */
   char key='1';
   ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY, (void*)&key);

   /* Даем, время, чтобы все блоки данных были получены звуковой картой.*/
   ms_sleep(2);   
}

Después de inicializar el transmisor de medios, se crean tres filtros: fuente vacía, dtmfgen, snd_card_write. Se crea una fuente de reloj.

Luego es necesario conectar los filtros de acuerdo con nuestro circuito, y la fuente del reloj debe conectarse en último lugar, ya que después de esto el circuito comenzará a funcionar inmediatamente. Si conecta una fuente de reloj a un circuito inacabado, puede suceder que el transmisor de medios falle si detecta al menos un filtro en la cadena con todas las entradas o todas las salidas "suspendidos en el aire" (no conectados).

La conexión de filtros se realiza mediante la función.

ms_filter_link(src, src_out, dst, dst_in)

donde el primer argumento es un puntero al filtro de origen, el segundo argumento es el número de salida de origen (tenga en cuenta que las entradas y salidas se numeran comenzando desde cero). El tercer argumento es un puntero al filtro del receptor, el cuarto es el número de entrada del receptor.

Todos los filtros están conectados y la fuente del reloj está conectada en último lugar (en adelante lo llamaremos simplemente ticker). Después de eso, nuestro circuito de sonido comienza a funcionar, pero todavía no se escucha nada en los parlantes de la computadora: el generador de sonido se apaga y simplemente pasa a través de los bloques de datos de entrada en silencio. Para comenzar a generar un tono, debe ejecutar el método de filtro del generador.

Generaremos una señal de dos tonos (DTMF) correspondiente a pulsar el botón "1" del teléfono. Para ello utilizamos la función ms_filter_call_method() Llamamos al método MS_DTMF_GEN_PLAY, pasándole como argumento un puntero al código al que debe corresponder la señal de reproducción.

Ya sólo queda compilar el programa:

$ gcc mstest2.c -o mstest2 `pkg-config mediastreamer --libs --cflags`

Y correr:

$ ./mstest2

Después de iniciar el programa, escuchará una breve señal sonora que consta de dos tonos en el altavoz de la computadora.

Construimos y lanzamos nuestro primer circuito de sonido. Vimos cómo crear instancias de filtro, cómo conectarlas y cómo llamar a sus métodos. Si bien estamos contentos con nuestro éxito inicial, aún debemos prestar atención al hecho de que nuestro programa no libera la memoria asignada antes de salir. En el proximo статье aprenderemos a limpiar lo que ensuciamos.

Fuente: habr.com

Añadir un comentario