欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言 ffmpeg與sdl實現(xiàn)播放視頻同時同步時鐘詳解

 更新時間:2022年09月24日 11:29:12   作者:CodeOfCC  
使用ffmpeg和sdl實現(xiàn)播放視頻后,需要再實現(xiàn)時鐘同步才能正常的播放視頻,尤其是有音頻的情況,我們通常需要將視頻同步到音頻來確保音畫同步

前言

視頻的時鐘同步有時是很難理解的,甚至知道了理論并不能確保實現(xiàn),需要通過實踐獲取各種參數(shù)以及具體的實現(xiàn)邏輯。本文將介紹一些視頻時鐘同步的具體實現(xiàn)方式。

一、直接延時

我們播放視頻是可以直接延時的,這種方式比較不準(zhǔn)確,但是也算是一種初級的方法。

1、根據(jù)幀率延時

每渲染一幀都進(jìn)行一個固定的延時,這個延時的時間是通過幀率計算得來。

//獲取視頻幀率
AVRational framerate = play->formatContext->streams[video->decoder.streamIndex]->avg_frame_rate;
//根據(jù)幀率計算出一幀延時
double duration = (double)framerate.num / framerate.den;
//顯示視頻
略
//延時
av_usleep(duration* 1000000);

2、根據(jù)duration延時

每渲染一幀根據(jù)其duration進(jìn)行延時,這個duration在視頻的封裝格式中通常會包含。

//獲取當(dāng)前幀的持續(xù)時間,下列方法有可能會無法獲取duration,還有其他方法獲取duration這里不具體說明,比如ffplay通過緩存多幀,來計算duartion。或者自己實現(xiàn)多幀估算duration。
AVRational timebase = play->formatContext->streams[video->decoder.streamIndex]->time_base;
double duration = frame->pkt_duration * (double)timebase.num / timebase.den;
//顯示視頻
略
//延時
av_usleep(duration* 1000000);

二、同步到時鐘

注:這一部分講的只有視頻時鐘同步不涉及音頻。

上面的簡單延時一定程度可以滿足播放需求,但也有問題,不能確保時間的準(zhǔn)確性。尤其是解復(fù)用解碼也會消耗時間,單純的固定延時會導(dǎo)致累計延時。這個時候我們就需要一個時鐘來校準(zhǔn)每一幀的播放時間。

1、同步到絕對時鐘

比較簡單的方式是按絕對的方式同步到時鐘,即視頻有多長,開始之后一定是按照系統(tǒng)時間的長度播放完成,如果視頻慢了則會不斷丟幀,直到追上時間。

定義一個視頻起始時間,在播放循環(huán)之外的地方。

//視頻起始時間,單位為秒
double videoStartTime=0;

播放循環(huán)中進(jìn)行時鐘同步。下列代碼的frame為解碼的AVFrame。

//以下變量時間單位為s	
//當(dāng)前時間
double currentTime = av_gettime_relative() / 1000000.0 - videoStartTime;
//視頻幀的時間
double	pts = frame->pts * (double)timebase.num / timebase.den;
//計算時間差,大于0則late,小于0則early。
double diff = currentTime - pts;
//視頻幀的持續(xù)時間,下列方法有可能會無法獲取duration,還有其他方法獲取duration這里不具體說明,比如ffplay通過緩存多幀,來計算duartion。
double duration = frame->pkt_duration * (double)timebase.num / timebase.den;
//大于閾值,修正時間,時鐘和視頻幀偏差超過0.1s時重新設(shè)置起點時間。
if (diff > 0.1)
{
	videoStartTime = av_gettime_relative() / 1000000.0 - pts;
	currentTime = pts;
	diff = 0;
}
//時間早了延時
if (diff < 0)
{
    //小于閾值,修正延時,避免延時過大導(dǎo)致程序卡死
    if (diff< -0.1)
    {
	    diff =-0.1;
    }
	av_usleep(-diff * 1000000);
	currentTime = av_gettime_relative() / 1000000.0 -videoStartTime;
	diff = currentTime - pts;
}
//時間晚了丟幀,duration為一幀的持續(xù)時間,在一個duration內(nèi)是正常時間,加一個duration作為閾值來判斷丟幀。
if (diff > 2 * duration)
{
	av_frame_unref(frame);
	av_frame_free(&frame);
	//此處返回即不渲染,進(jìn)行丟幀。也可以渲染追幀。
	return;
}
//顯示視頻
略

2、同步到視頻時鐘

同步到視頻時鐘就是,按照視頻播放的pts為基準(zhǔn),每次渲染的時候都根據(jù)當(dāng)前幀的pts更新視頻時鐘。與上面的差距只是多了最底部一行時鐘更新代碼。

//更新視頻時鐘
videoStartTime = av_gettime_relative() / 1000000.0 - pts;

因為與上一節(jié)代碼基本一致,所以不做具體說明,直接參考上一節(jié)說明即可。

//以下變量時間單位為s	
//當(dāng)前時間
double currentTime = av_gettime_relative() / 1000000.0 - videoStartTime;
//視頻幀的時間
double	pts = frame->pts * (double)timebase.num / timebase.den;
//計算時間差,大于0則late,小于0則early。
double diff = currentTime - pts;
//視頻幀的持續(xù)時間,下列方法有可能會無法獲取duration,還有其他方法獲取duration這里不具體說明,比如ffplay通過緩存多幀,來計算duartion。
double duration = frame->pkt_duration * (double)timebase.num / timebase.den;
//大于閾值,修正時間,時鐘和視頻幀偏差超過0.1s時重新設(shè)置起點時間。
if (diff > 0.1)
{
	videoStartTime = av_gettime_relative() / 1000000.0 - pts;
	currentTime = pts;
	diff = 0;
}
//時間早了延時
if (diff < 0)
{
    //小于閾值,修正延時,避免延時過大導(dǎo)致程序卡死
    if (diff< -0.1)
    {
	    diff =-0.1;
    }
	av_usleep(-diff * 1000000);
	currentTime = av_gettime_relative() / 1000000.0 - videoStartTime;
	diff = currentTime - pts;
}
//時間晚了丟幀,duration為一幀的持續(xù)時間,在一個duration內(nèi)是正常時間,加一個duration作為閾值來判斷丟幀。
if (diff > 2 * duration)
{
	av_frame_unref(frame);
	av_frame_free(&frame);
	//此處返回即不渲染,進(jìn)行丟幀。也可以渲染追幀。
	return;
}
//更新視頻時鐘
videoStartTime = av_gettime_relative() / 1000000.0 - pts;
//顯示視頻
略

三、同步到音頻

1、音頻時鐘的計算

要同步到音頻我們首先得計算音頻時鐘,通過音頻播放的數(shù)據(jù)長度可以計算出pts。

定義兩個變量,音頻的pts,以及音頻時鐘的起始時間startTime。

//下列變量單位為秒
double audioPts=0;
double audioStartTime=0;

在sdl的音頻播放回調(diào)中計算音頻時鐘。其中spec為SDL_AudioSpec是SDL_OpenAudioDevice的第四個參數(shù)。

//音頻設(shè)備播放回調(diào)
static void play_audio_callback(void* userdata, uint8_t* stream, int len) {
    //寫入設(shè)備的音頻數(shù)據(jù)長度
    int dataSize;
    //將數(shù)據(jù)拷貝到stream
    略
    //計算音頻時鐘
    if (dataSize > 0)
	{
	  //計算當(dāng)前pts
      audioPts+=(double) (dataSize)*/ (spec.freq * av_get_bytes_per_sample(forceFormat) * spec.channels);
      //更新音頻時鐘
      audioStartTime= = av_gettime_relative() / 1000000.0 -audioPts;
    }
}

2、同步到音頻時鐘

有了音頻時鐘后,我們需要將視頻同步到音頻,在二、2的基礎(chǔ)上加入同步邏輯即可。

//同步到音頻	
double avDiff = 0;
avDiff = videoStartTime - audioStartTime;
diff += avDiff;

完整代碼

//以下變量時間單位為s	
//當(dāng)前時間
double currentTime = av_gettime_relative() / 1000000.0 - videoStartTime;
//視頻幀的時間
double	pts = frame->pts * (double)timebase.num / timebase.den;
//計算時間差,大于0則late,小于0則early。
double diff = currentTime - pts;
//視頻幀的持續(xù)時間,下列方法有可能會無法獲取duration,還有其他方法獲取duration這里不具體說明,比如ffplay通過緩存多幀,來計算duartion。
double duration = frame->pkt_duration * (double)timebase.num / timebase.den;
//同步到音頻	
double avDiff = 0;
avDiff = videoStartTime - audioStartTime;
diff += avDiff;
//大于閾值,修正時間,時鐘和視頻幀偏差超過0.1s時重新設(shè)置起點時間。
if (diff > 0.1)
{
	videoStartTime = av_gettime_relative() / 1000000.0 - pts;
	currentTime = pts;
	diff = 0;
}
//時間早了延時
if (diff < 0)
{
    //小于閾值,修正延時,避免延時過大導(dǎo)致程序卡死
    if (diff< -0.1)
    {
	    diff =-0.1;
    }
	av_usleep(-diff * 1000000);
	currentTime = av_gettime_relative() / 1000000.0 - videoStartTime;
	diff = currentTime - pts;
}
//時間晚了丟幀,duration為一幀的持續(xù)時間,在一個duration內(nèi)是正常時間,加一個duration作為閾值來判斷丟幀。
if (diff > 2 * duration)
{
	av_frame_unref(frame);
	av_frame_free(&frame);
	//此處返回即不渲染,進(jìn)行丟幀。也可以渲染追幀。
	return;
}
//更新視頻時鐘
videoStartTime = av_gettime_relative() / 1000000.0 - pts;
//顯示視頻
略

總結(jié)

就是今天要講的內(nèi)容,本文簡單介紹了幾種視頻時鐘同步的方法,不算特別難,但是在網(wǎng)上查找的資料比較少??梢詤⒖嫉膄fplay的實現(xiàn)也有點復(fù)雜,本文的實現(xiàn)部分借鑒了ffplay。本文實現(xiàn)的時鐘同步還是可以繼續(xù)優(yōu)化的,比如用pid進(jìn)行動態(tài)控制。以及duration的計算可以細(xì)化調(diào)整。

到此這篇關(guān)于C語言 ffmpeg與sdl實現(xiàn)播放視頻同時同步時鐘詳解的文章就介紹到這了,更多相關(guān)C語言 ffmpeg與sdl內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++幾種基本的插入排序(圖文)

    c++幾種基本的插入排序(圖文)

    這篇文章主要介紹了c++幾種基本的插入排序(圖文),需要的朋友可以參考下
    2014-11-11
  • C++中Copy-Swap實現(xiàn)拷貝交換

    C++中Copy-Swap實現(xiàn)拷貝交換

    本文主要介紹了C++中Copy-Swap實現(xiàn)拷貝交換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 基于OpenCV實現(xiàn)圖像分割

    基于OpenCV實現(xiàn)圖像分割

    這篇文章主要為大家詳細(xì)介紹了基于OpenCV實現(xiàn)圖像分割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C語言如何利用異或進(jìn)行兩個值的交換詳解

    C語言如何利用異或進(jìn)行兩個值的交換詳解

    最近在工作中遇到了兩個值交換的需求,發(fā)現(xiàn)自己對異或有些忘記,所以索性寫出來,方便以后需要的時候參考學(xué)習(xí),下面這篇文章主要給大家介紹了關(guān)于C語言如何利用異或進(jìn)行兩個值的交換的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09
  • C++連接mysql的方法(直接調(diào)用C-API)

    C++連接mysql的方法(直接調(diào)用C-API)

    首先安裝mysql,點完全安裝,才能在在安裝目錄include找到相應(yīng)的頭文件,注意,是完全安裝,需要的朋友可以參考下
    2017-06-06
  • C語言數(shù)據(jù)類型與sizeof關(guān)鍵字

    C語言數(shù)據(jù)類型與sizeof關(guān)鍵字

    這篇文章主要介紹了C語言數(shù)據(jù)類型與sizeof關(guān)鍵字,C語言的數(shù)據(jù)類型包括基本類型、構(gòu)造類型、指針類型以及空類型,下文更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-04-04
  • Qt 進(jìn)度條的實現(xiàn)示例

    Qt 進(jìn)度條的實現(xiàn)示例

    進(jìn)度條在很多時候都可以用到,有時我們需要在表格,樹狀欄中直觀顯示任務(wù)進(jìn)度或消耗百分比,本文就詳細(xì)的介紹一下Qt 進(jìn)度條的使用實例,感興趣的可以了解一下
    2021-06-06
  • C++求最大公約數(shù)四種方法解析

    C++求最大公約數(shù)四種方法解析

    這篇文章主要為大家詳細(xì)介紹了C++求最大公約數(shù)四種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • C語言實現(xiàn)停車場管理系統(tǒng)

    C語言實現(xiàn)停車場管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)停車場管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 淺析C語言位域和位段

    淺析C語言位域和位段

    以下是對C語言中的位域和位段進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-08-08

最新評論