Explorando el motor VoIP de Mediastreamer2. Parte 11

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

Explorando el motor VoIP de Mediastreamer2. Parte 11

Mecanismo de movimiento de datos

  • Bloque de datos dblk_t
  • Mensaje mblk_t
  • Funciones para trabajar con mensajes mblk_t
  • Cola cola_t
  • Funciones para trabajar con colas queue_t
  • Conexión de filtros
  • Punto de señal del gráfico de procesamiento de datos.
  • Actividades detrás de escena del ticker
  • Búfer (MSBufferizer)
  • Funciones para trabajar con MSBufferizer

En el pasado статье Hemos desarrollado nuestro propio filtro. Este artículo se centrará en el mecanismo interno para mover datos entre filtros de transmisión de medios. Esto le permitirá escribir filtros sofisticados con menos esfuerzo en el futuro.

Mecanismo de movimiento de datos

El movimiento de datos en el transmisor de medios se realiza utilizando colas descritas por la estructura. cola_t. Cadenas de mensajes como mlk_t, que por sí mismos no contienen datos de señal, sino solo enlaces al mensaje anterior, siguiente y al bloque de datos. Además, quiero enfatizar especialmente que también hay un campo para un enlace a un mensaje del mismo tipo, que le permite organizar una lista de mensajes enlazados individualmente. Llamaremos tupla a un grupo de mensajes unidos por dicha lista. Así, cualquier elemento de la cola puede ser un único mensaje. mlk_t, y tal vez el encabezado de una tupla de mensajes mlk_t. Cada mensaje de tupla puede tener su propio bloque de datos de pupilo. Discutiremos por qué se necesitan las tuplas un poco más adelante.

Como se mencionó anteriormente, el mensaje en sí no contiene un bloque de datos; en cambio, solo contiene un puntero al área de memoria donde está almacenado el bloque. En esta parte, la imagen general del trabajo del transmisor de medios recuerda a la puerta del almacén de la caricatura "Monsters, Inc.", donde las puertas (enlaces a salas de datos) se mueven a una velocidad increíble a lo largo de transportadores elevados, mientras que las propias habitaciones permanecer inmóvil.

Ahora, avanzando por la jerarquía de abajo hacia arriba, consideremos en detalle las entidades enumeradas del mecanismo de transmisión de datos en el transmisor de medios.

bloque de datos dblk_t

El bloque de datos consta de un encabezado y un búfer de datos. El encabezado se describe mediante la siguiente estructura,

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

Los campos de la estructura contienen punteros al principio del búfer, al final del búfer y a la función para eliminar el búfer de datos. Último elemento en el encabezado db_ref — contador de referencia, si llega a cero, sirve como señal para borrar este bloque de la memoria. Si el bloque de datos fue creado por la función datosb_alloc() , entonces el búfer de datos se colocará en la memoria inmediatamente después del encabezado. En todos los demás casos, el búfer se puede ubicar en algún lugar separado. El búfer de datos contendrá muestras de señales u otros datos que queramos procesar con filtros.

Se crea una nueva instancia de un bloque de datos usando la función:

dblk_t *datab_alloc(int size);

Como parámetro de entrada se da el tamaño de los datos que almacenará el bloque. Se asigna más memoria para colocar un encabezado (estructura) al comienzo de la memoria asignada. datosb. Pero cuando se utilizan otras funciones, esto no siempre sucede; en algunos casos, el búfer de datos puede estar ubicado separado del encabezado del bloque de datos. Al crear una estructura, los campos se configuran para que su campo base_db señaló el comienzo del área de datos, y db_lim hasta su fin. Recuento de enlaces db_ref se establece en uno. El puntero de la función de borrado de datos se establece en cero.

Mensaje mlk_t

Como se indicó, los elementos de la cola son de tipo mlk_t, se define de la siguiente manera:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Estructura mlk_t contiene punteros al principio b_prev, b_siguiente, que son necesarios para organizar una lista doblemente enlazada (que es una cola cola_t).

Luego viene el puntero b_cont, que sólo se utiliza cuando el mensaje forma parte de una tupla. Para el último mensaje de la tupla, este puntero permanece nulo.

A continuación vemos un puntero a un bloque de datos. b_datap, para el cual existe el mensaje. Le siguen punteros al área dentro del búfer de datos del bloque. Campo b_rptr especifica la ubicación desde la cual se leerán los datos del búfer. Campo b_wptr indica la ubicación desde la que se realizarán las escrituras en el búfer.

Los campos restantes son de carácter de servicio y no se relacionan con el funcionamiento del mecanismo de transferencia de datos.

A continuación se muestra un único mensaje con el nombre. m1 y bloque de datos d1.
Explorando el motor VoIP de Mediastreamer2. Parte 11
La siguiente figura muestra una tupla de tres mensajes. m1, m1_1, m1_2.
Explorando el motor VoIP de Mediastreamer2. Parte 11

Funciones de mensajería mlk_t

Un nuevo mensaje mlk_t creado por la función:

mblk_t *allocb(int size, int pri); 

ella coloca un nuevo mensaje en la memoria mlk_t con un bloque de datos del tamaño especificado tamaño, segundo argumento - pri no se utiliza en esta versión de la biblioteca. Debería seguir siendo cero. Durante la operación de la función, se asignará memoria para la estructura del nuevo mensaje y se llamará a la función. mblk_init(), que restablecerá todos los campos de la instancia creada de la estructura y luego, usando lo mencionado anteriormente datosb_alloc(), creará un búfer de datos. Luego de lo cual se configurarán los campos de la estructura:

mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;

En la salida recibimos un nuevo mensaje con campos inicializados y un búfer de datos vacío. Para agregar datos a un mensaje, debe copiarlo al búfer del bloque de datos:

memcpy(msg->b_rptr, data, size);

donde datos es un puntero a la fuente de datos, y tamaño - su tamaño.
luego necesitas actualizar el puntero al punto de escritura para que apunte nuevamente al comienzo del área libre en el buffer:

msg->b_wptr = msg->b_wptr + size

Si necesita crear un mensaje desde un búfer existente, sin copiarlo, utilice la función:

mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); 

La función, después de crear el mensaje y la estructura del bloque de datos, configurará sus punteros a los datos en la dirección buf. Aquellos. En este caso, el búfer de datos no se encuentra detrás de los campos de encabezado del bloque de datos, como ocurría al crear un bloque de datos con la función datosb_alloc(). El búfer con los datos pasados ​​a la función permanecerá donde estaba, pero con la ayuda de punteros se adjuntará al encabezado recién creado del bloque de datos y, en consecuencia, al mensaje.

a un mensaje mlk_t Se pueden concatenar secuencialmente varios bloques de datos. Esto se hace mediante la función:

mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); 

mp — un mensaje al que se añadirá otro bloque de datos;
datos — puntero al bloque, cuya copia se agregará al mensaje;
tamaño - tamaño de datos;
almohadilla — una bandera que indica que el tamaño de la memoria asignada debe alinearse a lo largo de un límite de 4 bytes (el relleno se realizará con ceros).

Si hay suficiente espacio en el búfer de datos del mensaje existente, los nuevos datos se pegarán detrás de los datos que ya están allí. Si hay menos espacio libre en el búfer de datos del mensaje que tamaño, luego se crea un nuevo mensaje con un tamaño de búfer suficiente y los datos se copian en su búfer. Este es un mensaje nuevo, vinculado al original mediante un puntero. b_cont. En este caso, el mensaje se convierte en una tupla.

Si necesita agregar otro bloque de datos a la tupla, entonces necesita usar la función:

void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);

ella encontrará el último mensaje en la tupla (él tiene b_cont será nulo) y llamará a la función para este mensaje anexarb().

Puede averiguar el tamaño de los datos en un mensaje o tupla usando la función:

int msgdsize(const mblk_t *mp);

recorrerá todos los mensajes de la tupla y devolverá la cantidad total de datos en los buffers de datos de esos mensajes. Para cada mensaje, la cantidad de datos se calcula de la siguiente manera:

 mp->b_wptr - mp->b_rptr

Para combinar dos tuplas, use la función:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

ella agrega la tupla nuevo a la cola de la tupla mp y devuelve un puntero al último mensaje de la tupla resultante.

Si es necesario, una tupla se puede convertir en un mensaje con un solo bloque de datos; esto se hace mediante la función:

void msgpullup(mblk_t *mp,int len);

si el argumento len es igual a -1, entonces el tamaño del búfer asignado se determina automáticamente. Si len es un número positivo, se creará un búfer de este tamaño y los datos del mensaje de tupla se copiarán en él. Si el búfer se agota, la copia se detendrá allí. El primer mensaje de la tupla recibirá un búfer de nuevo tamaño con los datos copiados. Los mensajes restantes se eliminarán y la memoria se devolverá al montón.

Al eliminar una estructura mlk_t el recuento de referencia del bloque de datos se tiene en cuenta si, al llamar libreb() resulta ser cero, entonces el búfer de datos se elimina junto con la instancia mlk_t, que lo señala.

Inicializando los campos de un nuevo mensaje:

void mblk_init(mblk_t *mp);

Agregando otro dato al mensaje:

mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);

Si los nuevos datos no caben en el espacio libre del búfer de datos del mensaje, se adjunta al mensaje un mensaje creado por separado con un búfer del tamaño requerido (en el primer mensaje se establece un puntero al mensaje agregado) y el El mensaje se convierte en una tupla.

Agregar un dato a una tupla:

void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); 

La función llama a appendb() en un bucle.

Combinando dos tuplas en una:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

Mensaje nuevo se adjuntará a mp.

Hacer una copia de un solo mensaje:

mblk_t *copyb(const mblk_t *mp);

Copia completa de una tupla con todos los bloques de datos:

mblk_t *copymsg(const mblk_t *mp);

Los elementos de la tupla son copiados por la función. copiarb().

Crea una copia fácil de un mensaje mlk_t. En este caso, el bloque de datos no se copia, pero se aumenta su contador de referencia. db_ref:

mblk_t *dupb(mblk_t *mp);

Hacer una copia ligera de una tupla. Los bloques de datos no se copian, sólo se incrementan sus contadores de referencia. db_ref:

mblk_t *dupmsg(mblk_t* m);

Pegar todos los mensajes de una tupla en un solo mensaje:

void msgpullup(mblk_t *mp,size_t len);

Si el argumento len es -1, entonces el tamaño del búfer asignado se determina automáticamente.

Eliminando un mensaje, tupla:

void freemsg(mblk_t *mp);

El recuento de referencias del bloque de datos se reduce en uno. Si llega a cero, el bloque de datos también se elimina.

Cálculo de la cantidad total de datos en un mensaje o tupla.

size_t msgdsize(const mblk_t *mp);

Recuperar un mensaje del final de la cola:

mblk_t *ms_queue_peek_last (q);

Copiar el contenido de los campos reservados de un mensaje en otro mensaje (de hecho, estos campos contienen indicadores que utiliza el transmisor de medios):

mblk_meta_copy(const mblk_t *source, mblk *dest);

Cola cola_t

La cola de mensajes en el transmisor de medios se implementa como una lista circular doblemente enlazada. Cada elemento de la lista contiene un puntero a un bloque de datos con muestras de señales. Resulta que sólo los punteros al bloque de datos se mueven por turno, mientras que los datos en sí permanecen inmóviles. Aquellos. sólo se mueven los enlaces a ellos.
Estructura que describe la cola. cola_t, mostrado a continuación:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

La estructura contiene un campo: un puntero. _q_tapón escriba *mblk_t, apunta al primer elemento (mensaje) de la cola. El segundo campo de la estructura es el contador de mensajes en la cola.
La siguiente figura muestra una cola denominada q1 que contiene 4 mensajes m1, m2, m3, m4.
Explorando el motor VoIP de Mediastreamer2. Parte 11
La siguiente figura muestra una cola denominada q1 que contiene 4 mensajes m1,m2,m3,m4. El mensaje m2 es el encabezado de una tupla que contiene dos mensajes más m2_1 y m2_2.

Explorando el motor VoIP de Mediastreamer2. Parte 11

Funciones para trabajar con colas queue_t

Inicialización de cola:

void qinit(queue_t *q);

Campo _q_tapón (en adelante lo llamaremos “tope”) se inicializa mediante la función mblk_init(), su elemento anterior y el puntero del elemento siguiente se ajustan para que apunten a sí mismo. El contador de elementos de la cola se pone a cero.

Añadiendo un nuevo elemento (mensajes):

void putq(queue_t *q, mblk_t *m);

Nuevo elemento m se agrega al final de la lista, los punteros de los elementos se ajustan para que el tope se convierta en el siguiente elemento y se convierta en el elemento anterior del tope. Se incrementa el contador de elementos de la cola.

Recuperando un elemento de la cola:

mblk_t * getq(queue_t *q); 

Se recupera el mensaje que viene después del tapón y se disminuye el contador de elementos. Si no hay ningún elemento en la cola excepto el tapón, se devuelve 0.

Insertar un mensaje en una cola:

void insq(queue_t *q, mblk_t *emp, mblk_t *mp); 

Elemento mp insertado antes del elemento emp. Si emp=0, entonces el mensaje se agrega al final de la cola.

Recuperando un mensaje del principio de la cola:

void remq(queue_t *q, mblk_t *mp); 

El contador de elementos disminuye.

Leer un puntero al primer elemento de la cola:

mblk_t * peekq(queue_t *q); 

Eliminar todos los elementos de la cola mientras se eliminan los elementos mismos:

void flushq(queue_t *q, int how);

Argumento cómo no utilizado. El contador de elementos de la cola se establece en cero.

Macro para leer un puntero al último elemento de la cola:

mblk_t * qlast(queue_t *q);

Cuando trabaje con colas de mensajes, tenga en cuenta que cuando llame ms_queue_put(q,m) con un puntero nulo al mensaje, la función se repite. Su programa se congelará. se comporta de manera similar ms_queue_next(q,m).

Conexión de filtros

La cola descrita anteriormente se utiliza para pasar mensajes de un filtro a otro o de uno a varios filtros. Los filtros y sus conexiones forman un gráfico dirigido. La entrada o salida del filtro se denominará palabra general “pin”. Para describir el orden en que se conectan los filtros entre sí, el transmisor de medios utiliza el concepto de "punto de señal". El punto de señal es la estructura. _MSCPoint, que contiene un puntero al filtro y el número de uno de sus pines, por lo que describe la conexión de una de las entradas o salidas del filtro.

Punto de señal del gráfico de procesamiento de datos.

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

Los pines del filtro están numerados comenzando desde cero.

La conexión de dos pines mediante una cola de mensajes se describe mediante la estructura _MSQueue, que contiene una cola de mensajes y punteros a los dos puntos de señal que conecta:

typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;

Llamaremos a esta estructura enlace de señal. Cada filtro de transmisor de medios contiene una tabla de enlaces de entrada y una tabla de enlaces de salida (cola MS). El tamaño de las tablas se establece al crear un filtro; ya lo hemos hecho usando una variable exportada de tipo MSFilterDesc, cuando desarrollamos nuestro propio filtro. A continuación se muestra una estructura que describe cualquier filtro en un transmisor de medios, Filtro MS:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Después de conectar los filtros en el programa C de acuerdo con nuestro plan (pero no conectar el ticker), creamos un gráfico dirigido, cuyos nodos son instancias de la estructura. Filtro MS, y los bordes son instancias de enlaces cola MS.

Actividades detrás de escena del ticker

Cuando les dije que el ticker es un filtro para la fuente de tics, no era toda la verdad al respecto. Un ticker es un objeto que ejecuta funciones en el reloj. proceso() todos los filtros del circuito (gráfico) al que está conectado. Cuando conectamos un ticker a un filtro de gráfico en un programa C, le mostramos al ticker el gráfico que controlará a partir de ahora hasta que lo apaguemos. Después de conectarse, el ticker comienza a examinar el gráfico que se le ha confiado, compilando una lista de filtros que lo incluyen. Para no “contar” dos veces el mismo filtro marca los filtros detectados colocando un checkbox en ellos visto. La búsqueda se realiza utilizando las tablas de enlaces que tiene cada filtro.

Durante su recorrido introductorio por el gráfico, el ticker comprueba si entre los filtros hay al menos uno que actúa como fuente de bloques de datos. Si no hay ninguno, entonces el gráfico se considera incorrecto y el teletipo falla.

Si el gráfico resulta ser "correcto", para cada filtro encontrado, se llama a la función para su inicialización preprocesar(). Tan pronto como llega el momento del siguiente ciclo de procesamiento (cada 10 milisegundos por defecto), el ticker llama a la función proceso() para todos los filtros de origen encontrados anteriormente y luego para los filtros restantes de la lista. Si el filtro tiene enlaces de entrada, entonces ejecutar la función proceso() se repite hasta que las colas de enlaces de entrada estén vacías. Después de esto, pasa al siguiente filtro de la lista y lo "desplaza" hasta que los enlaces de entrada estén libres de mensajes. El ticker se mueve de un filtro a otro hasta que finaliza la lista. Esto completa el procesamiento del ciclo.

Ahora volveremos a las tuplas y hablaremos de por qué se agregó dicha entidad al transmisor de medios. En general, la cantidad de datos requerida por el algoritmo que opera dentro del filtro no coincide y no es múltiplo del tamaño de los buffers de datos recibidos en la entrada. Por ejemplo, estamos escribiendo un filtro que realiza una rápida transformada de Fourier, que por definición sólo puede procesar bloques de datos cuyo tamaño sea una potencia de dos. Que sean 512 cuentas. Si los datos se generan a través de un canal telefónico, entonces el buffer de datos de cada mensaje en la entrada nos traerá 160 muestras de señal. Es tentador no recopilar datos de la entrada hasta que exista la cantidad requerida de datos. Pero en este caso, se producirá una colisión con el ticker, que intentará sin éxito desplazarse por el filtro hasta que el enlace de entrada esté vacío. Anteriormente, designamos esta regla como el tercer principio del filtro. Según este principio, la función Process() del filtro debe tomar todos los datos de las colas de entrada.

Además, no será posible tomar solo 512 muestras de la entrada, ya que solo se pueden tomar bloques completos, es decir. el filtro tendrá que tomar 640 muestras y utilizar 512 de ellas, el resto antes de acumular una nueva porción de datos. Así, nuestro filtro, además de su trabajo principal, debe proporcionar acciones auxiliares para el almacenamiento intermedio de los datos de entrada. Los desarrolladores del transmisor de medios y la solución a este problema general han desarrollado un objeto especial: MSBufferizer (búfer), que resuelve este problema mediante tuplas.

Búfer (MSBufferizer)

Este es un objeto que acumulará datos de entrada dentro del filtro y comenzará a enviarlos para su procesamiento tan pronto como la cantidad de información sea suficiente para ejecutar el algoritmo de filtro. Mientras el búfer acumula datos, el filtro funcionará en modo inactivo, sin consumir la potencia de procesamiento del procesador. Pero tan pronto como la función de lectura del buffer devuelve un valor distinto de cero, la función Process() del filtro comienza a tomar y procesar datos del buffer en porciones del tamaño requerido, hasta que se agota.
Los datos que aún no son necesarios permanecen en el búfer como el primer elemento de la tupla, al que se adjuntan los bloques posteriores de datos de entrada.

La estructura que describe el buffer:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Funciones para trabajar con MSBufferizer

Creando una nueva instancia de buffer:

MSBufferizer * ms_bufferizer_new(void);

La memoria se asigna, se inicializa en ms_bufferizer_init() y se devuelve un puntero.

Función de inicialización:

void ms_bufferizer_init(MSBufferizer *obj); 

La cola se está inicializando. qcampo tamaño se pone a cero.

Agregando un mensaje:

void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); 

El mensaje m se agrega a la cola. El tamaño calculado de los bloques de datos se suma a tamaño.

Transferir todos los mensajes de la cola de datos del enlace al búfer q:

void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   

Transferir mensajes desde un enlace q en el buffer se realiza usando la función ms_bufferizer_put().

Leyendo del búfer:

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); 

Si el tamaño de los datos acumulados en el buffer es menor que el solicitado (datos), entonces la función devuelve cero, los datos no se copian en datos. De lo contrario, se realiza una copia secuencial de los datos de las tuplas ubicadas en el búfer. Después de copiar, la tupla se elimina y se libera la memoria. La copia finaliza en el momento en que se copian los bytes de datos. Si se acaba el espacio en medio de un bloque de datos, en este mensaje, el bloque de datos se reducirá a la parte restante sin copiar. La próxima vez que llame, la copia continuará desde este punto.

Leyendo la cantidad de datos que están actualmente disponibles en el búfer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Devuelve el campo tamaño buffer.

Descartando parte de los datos en el buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Se recupera y descarta el número especificado de bytes de datos. Los datos más antiguos se descartan.

Eliminar todos los mensajes en el buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

El contador de datos se pone a cero.

Eliminar todos los mensajes en el buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

El contador no se pone a cero.

Eliminando el buffer y liberando memoria:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Se pueden encontrar ejemplos del uso del buffer en el código fuente de varios filtros de transmisión de medios. Por ejemplo, en el filtro MS_L16_ENC, que reorganiza los bytes en las muestras desde el orden de la red hasta el orden del host: l16.c

En el próximo artículo, veremos el problema de la estimación de la carga de teletipos y cómo lidiar con la carga informática excesiva en el transmisor de medios.

Fuente: habr.com

Añadir un comentario