Explorando o mecanismo VoIP do Mediastreamer2. Parte 11

O material do artigo foi retirado do meu canal zen.

Explorando o mecanismo VoIP do Mediastreamer2. Parte 11

Mecanismo de movimentação de dados

  • Bloco de dados dblk_t
  • Mensagem mblk_t
  • Funções para trabalhar com mensagens mblk_t
  • Fila queue_t
  • Funções para trabalhar com filas queue_t
  • Conectando filtros
  • Ponto de sinal do gráfico de processamento de dados
  • Atividades nos bastidores do ticker
  • Bufferizador (MSBufferizer)
  • Funções para trabalhar com MSBufferizer

No passado статье desenvolvemos nosso próprio filtro. Este artigo se concentrará no mecanismo interno para movimentação de dados entre filtros de streamer de mídia. Isso permitirá que você escreva filtros sofisticados com menos esforço no futuro.

Mecanismo de movimentação de dados

A movimentação de dados no streamer de mídia é realizada usando filas descritas pela estrutura fila_t. Sequências de mensagens como mblk_t, que por si só não contêm dados de sinal, mas apenas links para a mensagem anterior, seguinte e para o bloco de dados. Além disso, quero enfatizar especialmente que também existe um campo para link para uma mensagem do mesmo tipo, que permite organizar uma lista de mensagens vinculadas individualmente. Chamaremos de tupla um grupo de mensagens unidas por tal lista. Assim, qualquer elemento da fila pode ser uma única mensagem mblk_t, e talvez o cabeçalho de uma tupla de mensagem mblk_t. Cada mensagem tupla pode ter seu próprio bloco de dados da ala. Discutiremos por que as tuplas são necessárias um pouco mais tarde.

Como mencionado acima, a mensagem em si não contém um bloco de dados; em vez disso, contém apenas um ponteiro para a área da memória onde o bloco está armazenado. Nesta parte, o quadro geral do trabalho do streamer de mídia lembra o armazém de portas do desenho animado “Monsters, Inc.”, onde portas (links para salas de dados) se movem a uma velocidade insana ao longo de transportadores aéreos, enquanto as próprias salas permanecer imóvel.

Agora, movendo-se ao longo da hierarquia de baixo para cima, consideraremos detalhadamente as entidades listadas do mecanismo de transmissão de dados no streamer de mídia.

Bloco de dados dblk_t

O bloco de dados consiste em um cabeçalho e um buffer de dados. O cabeçalho é descrito pela seguinte estrutura,

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

Os campos da estrutura contêm ponteiros para o início do buffer, o final do buffer e a função para excluir o buffer de dados. Último elemento no cabeçalho db_ref — contador de referência, se chegar a zero, serve como sinal para deletar este bloco da memória. Se o bloco de dados foi criado pela função datab_alloc() , o buffer de dados será colocado na memória imediatamente após o cabeçalho. Em todos os outros casos, o buffer pode estar localizado em algum lugar separado. O buffer de dados conterá amostras de sinais ou outros dados que queremos processar com filtros.

Uma nova instância de um bloco de dados é criada usando a função:

dblk_t *datab_alloc(int size);

Como parâmetro de entrada é dado o tamanho dos dados que o bloco irá armazenar. Mais memória é alocada para colocar um cabeçalho - estrutura - no início da memória alocada banco de dados. Mas ao usar outras funções, isso nem sempre acontece, em alguns casos, o buffer de dados pode estar localizado separadamente do cabeçalho do bloco de dados. Ao criar uma estrutura, os campos são configurados para que seu campo banco de dados_base apontou para o início da área de dados, e db_lim até o seu fim. Contagem de links db_ref está definido como um. O ponteiro da função de limpeza de dados é definido como zero.

mensagem mblk_t

Conforme declarado, os elementos da fila são do tipo mblk_t, é definido da seguinte forma:

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;

Estrutura mblk_t contém ponteiros no início b_prev, b_próximo, que são necessários para organizar uma lista duplamente vinculada (que é uma fila fila_t).

Então vem o ponteiro b_cont, que só é usado quando a mensagem faz parte de uma tupla. Para a última mensagem da tupla, esse ponteiro permanece nulo.

A seguir vemos um ponteiro para um bloco de dados b_datap, para o qual a mensagem existe. É seguido por ponteiros para a área dentro do buffer de dados do bloco. Campo b_rptr especifica o local de onde os dados do buffer serão lidos. Campo b_wptr indica o local a partir do qual as gravações no buffer serão executadas.

Os restantes campos são de natureza de serviço e não dizem respeito ao funcionamento do mecanismo de transferência de dados.

Abaixo está uma única mensagem com o nome m1 e bloco de dados d1.
Explorando o mecanismo VoIP do Mediastreamer2. Parte 11
A figura a seguir mostra uma tupla de três mensagens m1, m1_1, m1_2.
Explorando o mecanismo VoIP do Mediastreamer2. Parte 11

Funções de mensagens mblk_t

Uma nova mensagem mblk_t criado pela função:

mblk_t *allocb(int size, int pri); 

ela coloca uma nova mensagem na memória mblk_t com um bloco de dados do tamanho especificado tamanho, segundo argumento - peças não usado nesta versão da biblioteca. Deve permanecer zero. Durante a operação da função, será alocada memória para a estrutura da nova mensagem e a função será chamada mblk_init(), que irá zerar todos os campos da instância criada da estrutura e então, utilizando o mencionado acima datab_alloc(), criará um buffer de dados. Após o qual os campos da estrutura serão configurados:

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

Na saída recebemos uma nova mensagem com campos inicializados e um buffer de dados vazio. Para adicionar dados a uma mensagem, você precisa copiá-los para o buffer do bloco de dados:

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

onde dados, é um ponteiro para a fonte de dados e tamanho - seu tamanho.
então você precisa atualizar o ponteiro para o ponto de gravação para que ele aponte novamente para o início da área livre no buffer:

msg->b_wptr = msg->b_wptr + size

Se você precisar criar uma mensagem a partir de um buffer existente, sem copiar, use a função:

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

A função, após criar a mensagem e a estrutura do bloco de dados, irá configurar seus ponteiros para os dados no endereço buf. Aqueles. neste caso, o buffer de dados não está localizado após os campos de cabeçalho do bloco de dados, como foi o caso ao criar um bloco de dados com a função datab_alloc(). O buffer com os dados passados ​​​​para a função permanecerá onde estava, mas com a ajuda de ponteiros será anexado ao cabeçalho recém-criado do bloco de dados e, consequentemente, à mensagem.

Para uma mensagem mblk_t Vários blocos de dados podem ser concatenados sequencialmente. Isso é feito pela função:

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

mp — uma mensagem à qual será adicionado outro bloco de dados;
dados, — ponteiro para o bloco, cuja cópia será adicionada à mensagem;
tamanho — tamanho dos dados;
caminho — um sinalizador de que o tamanho da memória alocada deve ser alinhado ao longo de um limite de 4 bytes (o preenchimento será feito com zeros).

Se houver espaço suficiente no buffer de dados da mensagem existente, os novos dados serão colados atrás dos dados já existentes. Se houver menos espaço livre no buffer de dados da mensagem do que tamanho, uma nova mensagem será criada com um tamanho de buffer suficiente e os dados serão copiados para seu buffer. Esta é uma nova mensagem, vinculada à original usando um ponteiro b_cont. Neste caso, a mensagem se transforma em uma tupla.

Se precisar adicionar outro bloco de dados à tupla, você precisará usar a função:

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

ela encontrará a última mensagem na tupla (ele tem b_cont será nulo) e chamará a função para esta mensagem anexarb().

Você pode descobrir o tamanho dos dados em uma mensagem ou tupla usando a função:

int msgdsize(const mblk_t *mp);

ele percorrerá todas as mensagens na tupla e retornará a quantidade total de dados nos buffers de dados dessas mensagens. Para cada mensagem, a quantidade de dados é calculada da seguinte forma:

 mp->b_wptr - mp->b_rptr

Para combinar duas tuplas, use a função:

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

ela anexa a tupla novo para a cauda da tupla mp e retorna um ponteiro para a última mensagem da tupla resultante.

Se necessário, uma tupla pode ser transformada em uma mensagem com um único bloco de dados; isso é feito pela função:

void msgpullup(mblk_t *mp,int len);

se argumento len for -1, o tamanho do buffer alocado será determinado automaticamente. Se len for um número positivo, um buffer desse tamanho será criado e os dados da mensagem da tupla serão copiados para ele. Se o buffer acabar, a cópia irá parar aí. A primeira mensagem da tupla receberá um buffer de novo tamanho com os dados copiados. As mensagens restantes serão excluídas e a memória devolvida ao heap.

Ao excluir uma estrutura mblk_t a contagem de referência do bloco de dados é levada em consideração se, ao chamar grátis() acaba sendo zero, então o buffer de dados é excluído junto com a instância mblk_t, o que aponta para isso.

Inicializando os campos de uma nova mensagem:

void mblk_init(mblk_t *mp);

Adicionando outro dado à mensagem:

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

Se os novos dados não couberem no espaço livre do buffer de dados da mensagem, uma mensagem criada separadamente com um buffer do tamanho necessário será anexada à mensagem (um ponteiro para a mensagem adicionada é definido na primeira mensagem) e o mensagem se transforma em uma tupla.

Adicionando um dado a uma tupla:

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

A função chama appendb() em um loop.

Combinando duas tuplas em uma:

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

mensagem novo será anexado a mp.

Fazendo uma cópia de uma única mensagem:

mblk_t *copyb(const mblk_t *mp);

Cópia completa de uma tupla com todos os blocos de dados:

mblk_t *copymsg(const mblk_t *mp);

Os elementos da tupla são copiados pela função copiarb().

Crie uma cópia fácil de uma mensagem mblk_t. Neste caso, o bloco de dados não é copiado, mas seu contador de referência é aumentado db_ref:

mblk_t *dupb(mblk_t *mp);

Fazendo uma cópia leve de uma tupla. Os blocos de dados não são copiados, apenas seus contadores de referência são incrementados db_ref:

mblk_t *dupmsg(mblk_t* m);

Colando todas as mensagens de uma tupla em uma mensagem:

void msgpullup(mblk_t *mp,size_t len);

Se o argumento len for -1, o tamanho do buffer alocado será determinado automaticamente.

Excluindo uma mensagem, tupla:

void freemsg(mblk_t *mp);

A contagem de referência do bloco de dados é diminuída em um. Se chegar a zero, o bloco de dados também será excluído.

Cálculo da quantidade total de dados em uma mensagem ou tupla.

size_t msgdsize(const mblk_t *mp);

Recuperando uma mensagem do final da fila:

mblk_t *ms_queue_peek_last (q);

Copiar o conteúdo dos campos reservados de uma mensagem para outra mensagem (na verdade, esses campos contêm sinalizadores que são usados ​​pelo streamer de mídia):

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

Fila fila_t

A fila de mensagens no streamer de mídia é implementada como uma lista circular duplamente vinculada. Cada elemento da lista contém um ponteiro para um bloco de dados com amostras de sinal. Acontece que apenas os ponteiros para o bloco de dados se movem, enquanto os próprios dados permanecem imóveis. Aqueles. apenas links para eles são movidos.
Estrutura que descreve a fila fila_t, mostrado abaixo:

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

A estrutura contém um campo - um ponteiro _q_stopper digite *mblk_t, aponta para o primeiro elemento (mensagem) da fila. O segundo campo da estrutura é o contador de mensagens na fila.
A figura abaixo mostra uma fila denominada q1 contendo 4 mensagens m1, m2, m3, m4.
Explorando o mecanismo VoIP do Mediastreamer2. Parte 11
A figura a seguir mostra uma fila denominada q1 contendo 4 mensagens m1,m2,m3,m4. A mensagem m2 é o cabeçalho de uma tupla que contém mais duas mensagens m2_1 e m2_2.

Explorando o mecanismo VoIP do Mediastreamer2. Parte 11

Funções para trabalhar com filas queue_t

Inicialização da fila:

void qinit(queue_t *q);

Campo _q_stopper (doravante chamaremos de “rolha”) é inicializado pela função mblk_init(), seu elemento anterior e o ponteiro do próximo elemento são ajustados para apontar para si mesmo. O contador do elemento da fila é redefinido para zero.

Adicionando um novo elemento (mensagens):

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

Novo elemento m é adicionado ao final da lista, os ponteiros do elemento são ajustados para que a rolha se torne o próximo elemento e se torne o elemento anterior da rolha. O contador do elemento da fila é incrementado.

Recuperando um elemento da fila:

mblk_t * getq(queue_t *q); 

A mensagem que vem depois que o stopper é recuperado e o contador do elemento é decrementado. Se não houver elementos na fila, exceto o stopper, então 0 será retornado.

Inserindo uma mensagem em uma fila:

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

Elemento mp inserido antes do elemento emp. Se emp=0, então a mensagem é adicionada ao final da fila.

Recuperando uma mensagem do início da fila:

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

O contador de elementos é decrementado.

Lendo um ponteiro para o primeiro elemento da fila:

mblk_t * peekq(queue_t *q); 

Removendo todos os elementos da fila enquanto exclui os próprios elementos:

void flushq(queue_t *q, int how);

Argumento como não usado. O contador do elemento da fila é definido como zero.

Macro para leitura de um ponteiro para o último elemento da fila:

mblk_t * qlast(queue_t *q);

Ao trabalhar com filas de mensagens, esteja ciente de que ao ligar ms_queue_put(q,m) com um ponteiro nulo para a mensagem, a função faz um loop. Seu programa irá congelar. se comporta de forma semelhante ms_queue_next(q, m).

Conectando filtros

A fila descrita acima é usada para passar mensagens de um filtro para outro ou de um para vários filtros. Os filtros e suas conexões formam um gráfico direcionado. A entrada ou saída do filtro será chamada pela palavra geral “pin”. Para descrever a ordem em que os filtros são conectados entre si, o streamer de mídia usa o conceito de “ponto de sinal”. O ponto de sinal é a estrutura _MSCPoint, que contém um ponteiro para o filtro e o número de um de seus pinos; respectivamente, descreve a conexão de uma das entradas ou saídas do filtro.

Ponto de sinal do gráfico de processamento de dados

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

Os pinos do filtro são numerados começando do zero.

A conexão de dois pinos por uma fila de mensagens é descrita pela estrutura _MSQueue, que contém uma fila de mensagens e ponteiros para os dois pontos de sinal que ele conecta:

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

Chamaremos essa estrutura de link de sinal. Cada filtro de streamer de mídia contém uma tabela de links de entrada e uma tabela de links de saída (MSQueue). O tamanho das tabelas é definido ao criar um filtro; já fizemos isso usando uma variável exportada do tipo MSFilterDesc, quando desenvolvemos nosso próprio filtro. Abaixo está uma estrutura que descreve qualquer filtro em um streamer de mídia, 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;

Depois de conectarmos os filtros no programa C de acordo com nosso plano (mas não conectamos o ticker), criamos um gráfico direcionado, cujos nós são instâncias da estrutura Filtro MS, e as arestas são instâncias de links MSQueue.

Atividades nos bastidores do ticker

Quando eu disse a vocês que o ticker é um filtro para a origem dos ticks, não era toda a verdade sobre isso. Um ticker é um objeto que executa funções no relógio processo() todos os filtros do circuito (gráfico) ao qual está conectado. Quando conectamos um ticker a um filtro gráfico em um programa C, mostramos ao ticker o gráfico que ele controlará a partir de agora até que o desliguemos. Após a conexão, o ticker passa a examinar o gráfico que lhe foi confiado, compilando uma lista de filtros que o incluem. Para não “contar” o mesmo filtro duas vezes, marca os filtros detectados colocando uma caixa de seleção neles visto. A pesquisa é realizada através das tabelas de links que cada filtro possui.

Durante seu passeio introdutório pelo gráfico, o ticker verifica se entre os filtros existe pelo menos um que atue como fonte de blocos de dados. Se não houver nenhum, o gráfico será considerado incorreto e o ticker travará.

Se o gráfico estiver “correto”, para cada filtro encontrado, a função é chamada para inicialização pré-processar(). Assim que chega o momento do próximo ciclo de processamento (a cada 10 milissegundos por padrão), o ticker chama a função processo() para todos os filtros de origem encontrados anteriormente e, em seguida, para os filtros restantes na lista. Se o filtro tiver links de entrada, então executando a função processo() repete até que as filas do link de entrada estejam vazias. Depois disso, ele passa para o próximo filtro da lista e “rola” até que os links de entrada estejam livres de mensagens. O ticker move-se de filtro em filtro até que a lista termine. Isso completa o processamento do ciclo.

Agora retornaremos às tuplas e falaremos sobre por que tal entidade foi adicionada ao streamer de mídia. Em geral, a quantidade de dados exigida pelo algoritmo que opera dentro do filtro não coincide e não é um múltiplo do tamanho dos buffers de dados recebidos na entrada. Por exemplo, estamos escrevendo um filtro que executa uma transformada rápida de Fourier, que por definição só pode processar blocos de dados cujo tamanho seja uma potência de dois. Que sejam 512 contagens. Se os dados forem gerados por um canal telefônico, o buffer de dados de cada mensagem na entrada nos trará 160 amostras de sinal. É tentador não coletar dados da entrada até que a quantidade necessária de dados esteja disponível. Mas, neste caso, ocorrerá uma colisão com o ticker, que tentará, sem sucesso, rolar o filtro até que o link de entrada esteja vazio. Anteriormente designamos esta regra como o terceiro princípio do filtro. De acordo com este princípio, a função process() do filtro deve pegar todos os dados das filas de entrada.

Além disso, não será possível retirar apenas 512 amostras da entrada, pois só será possível retirar blocos inteiros, ou seja, o filtro terá que coletar 640 amostras e usar 512 delas, o restante antes de acumular uma nova porção de dados. Assim, nosso filtro, além de seu trabalho principal, deve fornecer ações auxiliares para armazenamento intermediário dos dados de entrada. Os desenvolvedores do streamer de mídia e da solução para esse problema geral desenvolveram um objeto especial - MSBufferizer (bufferer), que resolve esse problema usando tuplas.

Bufferizador (MSBufferizer)

Este é um objeto que acumulará os dados de entrada dentro do filtro e começará a enviá-los para processamento assim que a quantidade de informações for suficiente para executar o algoritmo do filtro. Enquanto o buffer acumula dados, o filtro operará em modo inativo, sem consumir o poder de processamento do processador. Mas assim que a função de leitura do buffer retorna um valor diferente de zero, a função process() do filtro começa a pegar e processar dados do buffer em porções do tamanho necessário, até que se esgotem.
Os dados que ainda não são necessários permanecem no buffer como o primeiro elemento da tupla, ao qual os blocos subsequentes de dados de entrada são anexados.

A estrutura que descreve o buffer:

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

Funções para trabalhar com MSBufferizer

Criando uma nova instância de buffer:

MSBufferizer * ms_bufferizer_new(void);

A memória é alocada, inicializada em ms_bufferizer_init() e um ponteiro é retornado.

Função de inicialização:

void ms_bufferizer_init(MSBufferizer *obj); 

A fila está inicializando q, campo tamanho está definido como zero.

Adicionando uma mensagem:

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

A mensagem m é adicionada à fila. O tamanho calculado dos blocos de dados é adicionado ao tamanho.

Transferindo todas as mensagens da fila de dados do link para o buffer q:

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

Transferindo mensagens de um link q no buffer é executado usando a função ms_bufferizer_put().

Lendo do buffer:

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

Se o tamanho dos dados acumulados no buffer for menor que o solicitado (dados), então a função retorna zero, os dados não são copiados para dados. Caso contrário, é realizada a cópia sequencial dos dados das tuplas localizadas no buffer. Após a cópia, a tupla é excluída e a memória é liberada. A cópia termina no momento em que os bytes de dados são copiados. Se o espaço acabar no meio de um bloco de dados, nesta mensagem, o bloco de dados será reduzido à parte restante não copiada. Na próxima vez que você ligar, a cópia continuará a partir deste ponto.

Lendo a quantidade de dados que está atualmente disponível no buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Retorna o campo tamanho buffer.

Descartando parte dos dados no buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

O número especificado de bytes de dados é recuperado e descartado. Os dados mais antigos são descartados.

Excluindo todas as mensagens no buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

O contador de dados é redefinido para zero.

Excluindo todas as mensagens no buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

O contador não é zerado.

Removendo o buffer e liberando memória:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Exemplos de uso do buffer podem ser encontrados no código-fonte de vários filtros de streamer de mídia. Por exemplo, no filtro MS_L16_ENC, que reorganiza os bytes nas amostras da ordem da rede para a ordem do host: l16.c

No próximo artigo, veremos a questão de estimar a carga em um ticker e maneiras de combater a carga excessiva de computação em um streamer de mídia.

Fonte: habr.com

Adicionar um comentário