[FFmpeg + OpenGL + OpenSL ES]音視訊同步- 8

we1less發表於2020-11-10

音視訊同步實現理論

PTS和time_base

PTS即顯示時間戳,這個時間戳用來告訴播放器該在什麼時候顯示這一幀的資料。time_base即時間度量單位(時間基),可以類比:米、千克這種單位。

分別獲取音訊和視訊的PTS(播放時間戳):

PTS = avFrame->pts * av_q2d(avStream->time_base);

獲取音視訊PTS差值,根據差值來設定視訊的睡眠時間達到和音訊的相對同步。視訊快了就休眠久點,視訊慢了就休眠少點,來達到同步。


在    Video.h     中宣告一個    audio    成員變數和    clock    成員屬性用來儲存當前視訊的時間戳    並且宣告一個函式用來計算音訊視訊的差值

//音訊物件
WlAudio *audio = NULL;

//時鐘
double clock = 0;
double getFrameDiffTime(AVFrame *avFrame);

在    Video.cpp    中寫方法宣告

double WlVideo::getFrameDiffTime(AVFrame *avFrame) {
    //返回當前avFrame的顯示時間戳
    double pts = av_frame_get_best_effort_timestamp(avFrame);
    if (pts == AV_NOPTS_VALUE) {
        pts = 0;
    }
    pts *= av_q2d(time_base);
    if (pts > 0) {
        clock = pts;
    }
    double diff = audio->clock - clock;
    return diff;
}

在    FFmpeg.cpp    中的    start    函式中將    audio    賦值給    video

//把video設定到視訊當中
video->audio = audio;

在    video.cpp    渲染之前呼叫函式計算diff

double diff = video->getFrameDiffTime(avFrame);

根據音訊和視訊時間戳的差值增大或減小視訊渲染的睡眠時長

在    video.h    中新建三個成員變數用來計算

//時鐘
double clock = 0;

double delayTime = 0;

double defaultDelayTime = 0.004;

宣告一個根據音視訊時間戳差值  增大或減小視訊渲染的睡眠時長的函式

 double getDelayTime(double diff);

在    video.cpp    寫這個函式的實現

double WlVideo::getDelayTime(double diff) {
    if (diff > 0.003) {
        delayTime = delayTime * 2 / 3;
        if (delayTime < defaultDelayTime / 2) {
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    } else if (diff < -0.003) {
        delayTime = delayTime * 3 / 2;
        if (delayTime < defaultDelayTime / 2) {
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    } else if (diff == 0.003) {

    }
    if (diff >= 0.5) {
        delayTime = 0;
    } else if (diff <= 0.5) {
        delayTime = defaultDelayTime * 2;
    }
    if (fabs(diff) > 10) {
        //音訊不存在
        delayTime = defaultDelayTime;
    }
    return delayTime;
}

在渲染下面呼叫

av_usleep(video->getDelayTime(diff) * 1000 * 1000);

 

相關文章