亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

一、直接延時(shí)

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

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

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

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

2、根據(jù)duration延時(shí)

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

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

二、同步到時(shí)鐘

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

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

1、同步到絕對(duì)時(shí)鐘

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

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

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

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

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

2、同步到視頻時(shí)鐘

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

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

因?yàn)榕c上一節(jié)代碼基本一致,所以不做具體說明,直接參考上一節(jié)說明即可。

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

三、同步到音頻

1、音頻時(shí)鐘的計(jì)算

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

定義兩個(gè)變量,音頻的pts,以及音頻時(shí)鐘的起始時(shí)間startTime。

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

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

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

2、同步到音頻時(shí)鐘

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

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

完整代碼

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

總結(jié)

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

    首先安裝mysql,點(diǎn)完全安裝,才能在在安裝目錄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)度條的實(shí)現(xiàn)示例

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

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

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

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

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

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

    淺析C語言位域和位段

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

最新評(píng)論