Serie de audio y video cinco sincronización de audio y video

breve descripción

El video y el audio se reproducen en hilos diferentes, porque factores como la programación de la CPU y la eficiencia de decodificación no se pueden sincronizar, por lo que se requiere una sincronización artificial. Hay tres tipos, video como punto de referencia, audio como punto de referencia y puntos de referencia de terceros. Debido a razones biológicas, las personas son más sensibles al sonido, por lo que es inconveniente ajustar el audio y, por lo general, se basa en el audio.
DTS : Marca de tiempo de decodificación, marca de tiempo de decodificación, indica el orden de decodificación del paquete del decodificador.
PTS : Sello de tiempo de presentación, que muestra el sello de tiempo, que indica el orden de visualización de los datos decodificados del paquete.
Los datos se almacenan secuencialmente en audio, al igual que los dos anteriores. En video, debido a que algunos fotogramas intermedios dependen de los fotogramas frontal y posterior, los fotogramas posteriores dependientes se almacenan delante de los fotogramas intermedios, lo que genera la diferencia entre los dos anteriores.
time_base : base de tiempo, la unidad de conversión de marca de tiempo y segundo de tiempo, la estructura AVRational almacena el numerador y el denominador. En cuanto a por qué el doble no se almacena directamente, debe convertirse a través de la función av_q2d, probablemente debido al problema de precisión, probablemente.

/**
 * Rational number (pair of numerator and denominator).
 */
typedef struct AVRational{
    
    
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;
    /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented.
     *
     * decoding: set by libavformat
     * encoding: May be set by the caller before avformat_write_header() to
     *           provide a hint to the muxer about the desired timebase. In
     *           avformat_write_header(), the muxer will overwrite this field
     *           with the timebase that will actually be used for the timestamps
     *           written into the file (which may or may not be related to the
     *           user-provided one, depending on the format).
     */
    AVRational time_base;

Por ejemplo, el tiempo de visualización de un cuadro determinado se puede calcular mediante la siguiente fórmula (la unidad de videoClock resultante es segundos)

videoClock = pts * av_q2d(time_base);

reloj de audio

Definir variables primero

AVRational audioTimeBase;
double audioClock;//音频时钟

Luego obtenga la base de tiempo de audio cuando obtenga la transmisión de audio

if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
    
    
            audio_index = i;
            audioTimeBase = avFormatContext->streams[i]->time_base;
        }

Finalmente, actualice el reloj de audio en la función getPcm

if (audioFrame->pts != AV_NOPTS_VALUE) {
    
    
	//这一帧的起始时间
    audioClock = audioFrame->pts * av_q2d(audioTimeBase);
    //这一帧数据的时间
    double time = size / ((double) 44100 * 2 * 2);
    //最终音频时钟
    audioClock = time + audioClock;
 }

Audio de sincronización de vídeo

Definir variables primero

AVRational videoTimeBase;
double videoClock;//视频时钟

Obtenga la base de tiempo del video al obtener la transmisión de video como se indica arriba

if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
    
    
            video_index = i;
            videoTimeBase = avFormatContext->streams[i]->time_base;
        }

En la función de bucle para tomar cuadros de video, defina variables fuera del bucle

    double last_play  //上一帧的播放时间
    , play             //当前帧的播放时间
    , last_delay    // 上一次播放视频的两帧视频间隔时间
    , delay         //线程休眠时间
    , diff   //音频帧与视频帧相差时间
    , sync_threshold //合理的范围
    , pts
    , decodeStartTime //每一帧解码开始时间
    , frame_time_stamp = av_q2d(videoTimeBase); //时间戳的实际时间单位

Calcule el tiempo de suspensión y sincronice en el ciclo de obtención de paquetes. Principalmente extendiendo o acortando el tiempo de visualización de cada cuadro

            decodeStartTime = av_gettime() / 1000000.0;
            AVPacket *packet = videoPacketQueue.front();
            videoPacketQueue.pop();
            avcodec_send_packet(avCodecContext, packet);
            AVFrame *frame = av_frame_alloc();
            if (!avcodec_receive_frame(avCodecContext, frame)) {
    
    
                if ((pts = frame->best_effort_timestamp) == AV_NOPTS_VALUE) {
    
    
                    pts = videoClock;
                }
                play = pts * frame_time_stamp;
                videoClock =
                        play + (frame->repeat_pict * 0.5 * frame_time_stamp + frame_time_stamp);
                delay = play - last_play;
                diff = videoClock - audioClock;
                sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
                if (fabs(diff) < 10) {
    
    
                    if (diff <= -sync_threshold) {
    
    
                        delay = FFMAX(0.01, delay + diff);
                    } else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD) {
    
    
                        delay = delay + diff;
                    } else if (diff >= sync_threshold && delay) {
    
    
                        delay = 2 * delay;
                    }
                }
                if (delay <= 0 || delay > 1) {
    
    
                    delay = last_delay;
                }
                last_delay = delay;
                //减去解码消耗时间
                delay = delay + (decodeStartTime - av_gettime() / 1000000.0);
                if (delay < 0) {
    
    
                    delay = 0;
                }
                last_play = play;

Finalmente dormir después de lanzar cuadros de video

                ANativeWindow_unlockAndPost(nativeWindow);
                if (delay > 0.001) {
    
    
                    av_usleep(delay * 1000000);
                }

Aunque todavía hay algunos detalles que deben mejorarse, en general es así.

código fuente

código fuente

Supongo que te gusta

Origin blog.csdn.net/Welcome_Word/article/details/120051643
Recomendado
Clasificación