[Combate FFmpeg] Análisis del número de serie de FFplay

Autor: Loken1 Enlace: https://juejin.cn/post/7155351642731855909

El número de serie está preparado principalmente para las funciones de avance rápido y rebobinado . Si no puede avanzar o rebobinar rápidamente, en realidad no necesita un número de serie. Mientras el subproceso demultiplexación continúe leyendo el AVPacket y lo coloque en la cola PacketQueue, el subproceso de decodificación continúa obteniendo datos de PacketQueue para decodificarlos y colocarlos en FrameQueue, y finalmente hay un subproceso de reproducción del que recuperar los datos. FrameQueue para la reproducción.

El diagrama de flujo general es el siguiente:

imagen

PacketQueue y FrameQueue son colas de caché , que primero extraen los datos que están listos para su uso. Pero solo porque se agregan las funciones de avance rápido y rebobinado, una vez que la reproducción salta a otro punto de tiempo, ¿los datos previamente preparados quedarán inutilizables?

Por lo tanto, se necesita un número de serie para determinar si los datos previamente almacenados en caché son los más recientes, por lo que el número de serie puede considerarse lo mismo que el número de versión .

El reproductor FFplay tiene principalmente 4 campos de números de serie, que son los siguientes:

1. Serie de la estructura PackeQueue, que es el número de serie de la propia cola. Puede considerarse como el valor del último número de serie.

2. El número de serie de la estructura MyAVPacketList es el número de serie del AVPacket en la cola.

3. pkt_serial de struct Decoder registra el número de serie del AVPacket utilizado por el decodificador la última vez para decodificar.

4. El número de serie de struct Frame, el número de serie de AVFrame en la cola.

Primero, la fuente serial de MyAVPacketList y la fuente serial de PackeQueue se pueden ver en la función paquete_queue_put_private():

imagen

El número de serie de Frame se deriva del pkt_serial de Decoder, de la siguiente manera:

imagen

¿Y de dónde viene pkt_serial en la estructura del Decoder?

Respuesta: El valor se asigna en decoder_decode_frame(). Lo que se registra es el número de serie de la entrada AVPacket más reciente al decodificador, de la siguiente manera:

imagen

Por lo tanto, se puede decir que el número de serie de Frame se deriva indirectamente del número de serie de AVPacket.

Ahora que hemos entendido los lugares donde se asignan los cuatro números de serie, ¿en qué códigos se utilizan estos cuatro campos de números de serie?

Respuesta: Siempre que se trate de la lógica relacionada con el almacenamiento en caché, se determinará el número de serie.

Debido a que el número de serie está preparado para las funciones de avance y rebobinado rápido, expliquemos brevemente la lógica del avance y rebobinado rápido. Para un análisis más detallado, consulte " Reproducción de punto de tiempo de salto de FFplay ".

Cuando presiona una de las teclas arriba, abajo, izquierda o derecha, se producirá una operación de búsqueda, que se implementa llamando a la función stream_seek(), de la siguiente manera:

imagen

El código de la función stream_seek() es el siguiente:

/* seek in the stream */
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
{
    if (!is->seek_req) {
        is->seek_pos = pos;
        is->seek_rel = rel;
        is->seek_flags &= ~AVSEEK_FLAG_BYTE;
        if (seek_by_bytes)
            is->seek_flags |= AVSEEK_FLAG_BYTE;
        is->seek_req = 1;
        SDL_CondSignal(is->continue_read_thread);
    }
}

El objetivo del código anterior es establecer la etiqueta is->seek_req y dónde saltar para jugar. Entonces se despertará el hilo read_thread(). Cuando el hilo read_thread() ve la marca is->seek_req, iniciará la operación de búsqueda. como sigue:

Por lo tanto, el lugar real donde se realiza la operación de búsqueda es el hilo demultiplexor read_thread(), no el hilo principal.

imagen

Como se puede ver en la figura anterior, después de usar avformat_seek_file() para realizar una operación de búsqueda, se llamará a la función paquete_queue_flush() para actualizar el número de secuencia y lograr el efecto de descartar el caché no válido anterior.

pack_queue_flush() es una función muy importante, el código es el siguiente:

imagen

Como puede ver, los datos en la cola PacketQueue se borran al principio y luego el número de secuencia es +1.

Personalmente tengo una pregunta: dado que los datos se borraron antes de +1, el hilo de decodificación no necesita determinar el número de secuencia en absoluto al recuperar datos de PakcetQueue. Debido a que se ha borrado, el hilo de decodificación no puede obtener el AVPacket antiguo.

Ignore esto por ahora; de todos modos, el número de serie de PacketQueue se ha actualizado.

El número de secuencia de serie de la cola PacketQueue se actualiza en +1, lo que provocará una serie de reacciones en cadena .

En primer lugar, la serie de MyAVPacketList agregada a la cola PacketQueue por el hilo de demultiplexación posterior también será la más reciente. Sin embargo, pkt_serial en el administrador de decodificación (struct Decoder) sigue siendo el valor anterior.

Como se mencionó anteriormente, pkt_serial de struct Decoder registra el número de serie del último AVPacket decodificado.

Esto conducirá a abandonar directamente el caché en el decodificador y utilizar el nuevo AVPacket para decodificar, de la siguiente manera:

imagen

También hará que se vacíe la caché del decodificador, de la siguiente manera:

imagen

La lógica de la figura anterior es que siempre que el número de serie del AVPacket decodificado esta vez sea diferente del número de serie del AVPacket utilizado la última vez, la memoria caché del decodificador se actualizará inmediatamente .

Otra área afectada por el encadenamiento es la lógica de lectura de FrameQueue. FrameQueue es diferente de PakcetQueue. FrameQueue en sí no tiene un número de serie, pero la estructura Frame en su cola tiene un número de serie, por lo que también se verá afectada, de la siguiente manera:

imagen

Se puede ver que cuando el hilo de reproducción de audio obtiene datos, determinará si el número de serie del marco es el último y, de lo contrario, se descartará inmediatamente.

Existe otro escenario de aplicación para números de serie, el reloj, como se muestra a continuación:

typedef struct Clock {
       ...
    int serial;           /* clock is based on a packet with this serial */
    ...
    int *queue_serial;    /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;

Este escenario se utiliza al sincronizar audio y video, lo cual es un poco complicado. Cuando más adelante expliquemos la sincronización de audio y vídeo, analizaremos el número de serie en Reloj.

  >>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
  >>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群 739729163 领取

Supongo que te gusta

Origin blog.csdn.net/weixin_52622200/article/details/131684966
Recomendado
Clasificación