使用timeBeginPeriod設置Sleep精度php
timeBeginPeriod(1);
timeEndPeriod(1); linux
1. 音視頻同步的原理編程
2. 音視頻同步的較正方法緩存
3. 音視頻同步時遇到的問題安全
聲明:如下全部內容均爲轉載多線程
1.原文轉自:http://bbs.chinavideo.org/viewthread.php?tid=1183&extra=page%3D1%26amp%3Bfilter%3Ddigest&page=1ide
下面貼出部分:函數
音視頻同步這一塊,我一直不是很瞭解,很想嘗試一下,下面是在qq羣上向諸位前人請教的會話記錄,由於對我這樣的初學者頗有幫助,因此粘貼至此,以做備份,再次感謝熱心回答問題的人,尤爲是(不作好人!)^_^;
2006-10-25 10:21:06 dophin
有人有實時編碼時對音視頻進行錄製時,音視頻如何同步的相關資料麼?有相關網站也能夠哈,我實在找不到相關資料了。。。
2006-10-25 10:25:42 dophin
或者具體一點的也行,好比音視頻究竟是如何同步的,有書籍介紹也能夠啊,先謝了哈。。。。
(人衆人) 2006-10-25 10:21:45
經過時間戳
(人衆人) 2006-10-25 10:21:49
我也在研究這個問題
( 新左派) 2006-10-25 10:21:56
以音頻時間爲主
(人衆人) 2006-10-25 10:22:01
對
(不作好人!) 2006-10-25 10:22:08
新左派講對了。
(不作好人!) 2006-10-25 10:22:14
聲音圖象交錯發送。
( 新左派) 2006-10-25 10:22:43
視頻解碼時,按當前播放時間找到對應的視頻幀
(不作好人!) 2006-10-25 10:22:44
能夠一個音頻包有N個圖象。就在這一個音頻包完成的過程當中按幀率顯示圖象。
(dophin) 2006-10-25 10:28:39
嗯,那麼音頻又是以什麼爲主呢?畢竟,音頻的播放也是和時間有關係的,音頻在播放時就是隻管本身播放就行麼?
(不作好人!) 2006-10-25 10:24:05
聲卡有時間同步處理機制的。
(不作好人!) 2006-10-25 10:24:32
我之前的電腦主板若是驅動沒有裝好。聲音就很是快。結果圖象也是一閃就過去了。
( 新左派) 2006-10-25 10:24:47
聲音正常解碼與播放啊
(不作好人!) 2006-10-25 10:25:05
是WINDOWS MEDIAPLAYER播放的。只有驅動裝好了。能正確驅動的時候聲卡有時鐘同步功能的。
( 新左派) 2006-10-25 10:25:14
只是視頻的解碼須要參照當前的聲音的時間
(不作好人!) 2006-10-25 10:25:16
因此聲音和圖象交錯,
( 新左派) 2006-10-25 10:25:36
聲音播放時,是不用管什麼的,就是單獨的聲音解碼
(dophin) 2006-10-25 10:32:16
哦,我如今大腦中大體有點模型了,就是音頻只管本身播放就行,視頻根據本身自己帶的時間戳與但前系統時間和音頻時間進行比較,而後解碼播放,如此實現同步,這麼理解對麼?
(不作好人!) 2006-10-25 10:28:00
你是用什麼系統?
(dophin) 2006-10-25 10:33:26
聲音和圖像交錯是什麼意思?這兩個不是開兩個線程完成的麼?不存在交錯問題啊,即便存在,也是操做系統級的吧 ?
(不作好人!) 2006-10-25 10:28:35
AVVVVVVVVVVVVVVVAVVVVVVVVVVVVVVV
(dophin) 2006-10-25 10:33:42
我得,linux。。。
(不作好人!) 2006-10-25 10:28:52
當你放A的時候,直接交給聲卡。
(不作好人!) 2006-10-25 10:29:30
中間的V就是先後兩個聲音包的相差時間,你就算出平均速度
(dophin) 2006-10-25 10:35:35
算出 平均速度後,得出每sec放幾幀,而後播放v,是這樣麼?
(不作好人!) 2006-10-25 10:30:54
對
(不作好人!) 2006-10-25 10:31:01
你聲音有一個採樣率吧。
(dophin) 2006-10-25 10:36:02
對,有,
(不作好人!) 2006-10-25 10:31:23
這就能夠算出先後一個聲音的時間了對不?
(不作好人!) 2006-10-25 10:31:36
好比44.1K/S
(dophin) 2006-10-25 10:36:36
對,
(不作好人!) 2006-10-25 10:31:56
那你從DSP中讀取22.05K的數據是否是0.5?
(dophin) 2006-10-25 10:37:05
是,
(不作好人!) 2006-10-25 10:32:29
那在這段0.5秒鐘的時間內你獲取了15幀的數據。那你是否是0.5/15=0.03333秒鐘就刷新一副圖。
(dophin) 2006-10-25 10:37:49
沒錯。
(不作好人!) 2006-10-25 10:32:55
而後你再讀取下一個聲音和圖象包。再這樣搞。就能夠了啦。不過前提條件你採集必須是同步的。
(不作好人!) 2006-10-25 10:33:11
對了。你是用嵌入式的嗎?
(不作好人!) 2006-10-25 10:33:25
這個是以聲音爲基礎的。
(不作好人!) 2006-10-25 10:34:04
還有一種是設置時鐘,計算你的幀率來設置先後幀的時間,中間有偏差就延時或者是跳躍一下。聲音就另外單獨管理。
(不作好人!) 2006-10-25 10:34:21
看看ffmpeg 的fplay.c,裏面的源程序講得很詳細了。
(dophin) 2006-10-25 10:40:12
個人不是embedded
(dophin) 2006-10-25 10:40:24
就是普通pc機,
(不作好人!) 2006-10-25 10:36:08
哦。那你看fplay.c吧。裏面很詳細的有聲音和圖象的同步。雖然簡單了一點。但麻雀雖小五臟俱全。
(dophin) 2006-10-25 10:41:54
哦,太好了,採集這一塊,我基本明白了,播放我想也應該差很少,我本身試試看看,太感謝了你了哈。。。^_^
(不作好人!) 2006-10-25 10:37:14
別謝謝我。
(不作好人!) 2006-10-25 10:37:24
不過我作的方法是按照fplay.c裏面的作的。
(dophin) 2006-10-25 10:42:23
好的,我立刻就看看fplay。c,之前沒有看是由於感受他的播放效果不如mplayer,
(不作好人!) 2006-10-25 10:38:06
這個是簡化了的啦。固然比不上mplayer性能
這幾天搞文件回放,視頻格式是 H264,音頻是PCM,使用FFMPEG來讀取音視頻,而後用ffmpeg來解碼顯示,全部的一切還算順利,但音視頻同步花了我不少時間,總也搞不清楚 爲何會差不少。音視頻同步的原理固然是根據音頻的pts來控制視頻的播放,也就是說在視頻解碼一幀後,是否顯示以及顯示多長時間是經過該幀的PTS與同 時正在播放的音頻的PTS比較而來的,若是音頻的PTS較大,則視頻顯示完畢準備下一幀的解碼顯示,不然等待。
具體實現時遇到的問題一:沒辦法獲得正在播放的音頻幀的PTS,由於進行音頻播放使用的DirectSound,而對於DirectSound我只能獲得 當前拷入DirectSound的緩存的幀的PTS,而沒法獲得正在播放的PTS,若是得不到正在播放的幀的PTS的話,那同步確定是不可能的了。在網上 找資料好象也沒找到有用的,最後忽然想到因爲音頻幀的大小與時間成正比,那麼DirectSound的緩存中的數據所須要的播放時間就能夠計算得出,再根 據當前正在拷入的音頻幀的PTS,就能夠獲得正在播放的幀的PTS,再用這個就能夠正確同步視頻幀的顯示了。
問題二:根據上面的方法處理後仍是出現不一樣步的現象,爲何這樣我也是百思不得其解,後來才發現是等待機制有問題,原來我是用Sleep()來作等待的, 但實際上Sleep()的偏差很大的,網上有說有15MS,作音視頻同步確定是不行的了,通過不斷的google,找到一份代碼:
void MySleep(int interval)
{
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 得到計數器的時鐘頻率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 得到初始值網站
do
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//得到停止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 得到對應的時間值,單位爲秒
}while(dfTim<0.001 * interval);
}
能夠達到精度比較高的等待,從效果看,也能夠達到音視頻同步。
本覺得問題到這就算結束了,但程序運行的時候怎麼發現機器這麼慢呀,看了下CPU佔用率,達到100%。很顯然使用這個作等待是不行的了。
因而繼續google,網上有說timesetevent什麼的,我沒有試。感受麻煩了些。後來想到之前看過的一篇用 WaitForSingleObject來作定時讓某段代碼執行的,因而試了試,一試之下當即發現效果明顯,CPU佔用率一會兒回到了個位數。更改後的代 碼以下:
void MySleep(int interval)
{
HANDLE evt;
evt = CreateEvent(NULL, TRUE, FALSE, NULL);
WaitForSingleObject(evt, interval);
CloseHandle(evt);
}
4.如下轉至 http://blog.csdn.net/stone_kingnet/article/details/3111171
媒 體內容在播放時,最使人頭痛的就是音視頻不一樣步。從技術上來講,解決音視頻同步問題的最佳方案就是時間戳:首先選擇一個參考時鐘(要求參考時鐘上的時間是 線性遞增的);生成數據流時依據參考時鐘上的時間給每一個數據塊都打上時間戳(通常包括開始時間和結束時間);在播放時,讀取數據塊上的時間戳,同時參考當 前參考時鐘上的時間來安排播放(若是數據塊的開始時間大於當前參考時鐘上的時間,則不急於播放該數據塊,直到參考時鐘達到數據塊的開始時間;若是數據塊的 開始時間小於當前參考時鐘上的時間,則「儘快」播放這塊數據或者索性將這塊數據「丟棄」,以使播放進度追上參考時鐘)。
圖2.8 解決音視頻同步問題的時間戳方案
可見,避免音視頻不一樣步現象有兩個關鍵——一是在生成數據流時要打上正確的時間戳。若是數據塊上打的時間戳自己就有問題,那麼播放時再怎麼調整也於事無補。如圖2.8,視頻流內容是從0s開始的,假設10s時有人開始說話,要求配上音頻流,那麼音頻流的起始時間應該是10s,若是時間戳從0s或 其它時間開始打,則這個混合的音視頻流在時間同步上自己就出了問題。打時間戳時,視頻流和音頻流都是參考參考時鐘的時間,而數據流之間不會發生參考關係; 也就是說,視頻流和音頻流是經過一箇中立的第三方(也就是參考時鐘)來實現同步的。第二個關鍵的地方,就是在播放時基於時間戳對數據流的控制,也就是對數 據塊早到或晚到採起不一樣的處理方法。圖2.8中,參考時鐘時間在0-10s內播放視頻流內容過程當中,即便收到了音頻流數據塊也不能當即播放它,而必須等到參考時鐘的時間達到10s以後才能夠,不然就會引發音視頻不一樣步問題。
基於時間戳的播放過程當中,僅僅對早到的或晚到的數據塊進行等待或快速處理,有時候是不夠的。若是想要更加主動而且有效地調節播放性能,須要引入一個反饋機制,也就是要將當前數據流速度太快或太慢的狀態反饋給「源」,讓源去放慢或加快數據流的速度。熟悉DirectShow的讀者必定知道,DirectShow中的質量控制(Quality Control)就是這麼一個反饋機制。DirectShow對於音視頻同步的解決方案是至關出色的。但WMF SDK在播放時只負責將ASF數據流讀出並解碼,而並不負責音視頻內容的最終呈現,因此它也缺乏這樣的一個反饋機制。
爲了更好地理解基於時間戳的音視頻同步方案,下面舉一個生活中的例子。假設你和你的一個朋友約好了今天18:00在滬上廣場見面,而後一塊兒吃飯,再去打遊戲。實際上,這個18:00就是你和你朋友保持同步的一個時間點。結果你17:50就到了滬上廣場,那麼你必須等你的朋友。10分 鍾事後,你的朋友尚未到,這時他打來電話說有事耽擱了,要晚一點才能到。你沒辦法,由於你已經在旁邊的餐廳預訂了位置,若是不立刻趕過去,預訂就會被取 消,因而你告訴你的朋友直接到餐廳碰頭吧,要他加快點。因而在餐廳未來的某個時間點就成爲你和你朋友的又一個同步點。雖然具體時間不定(要看你朋友趕過來 的速度),但這樣努力的方向是對的,你和你朋友確定能在餐廳見到面。結果呢?你朋友終於在18:30趕過來了,大家最終「同步」了。吃完飯19:30了,你臨時有事要處理一下,因而跟你朋友再約好了20:00在附近的一家遊戲廳碰頭。大家又不一樣步了,但在遊戲廳未來的某個時間點大家仍是會再次同步的。
其實,同步是一個動態的過程,是一個有人等待、有人追趕的過程。同步只是暫時的,而不一樣步纔是常態。人們老是在同步的水平線上振盪波動,但不會偏離這條基線太遠。
在發送方:
對於相同時刻的音頻/視頻幀,打上相同的時間戳(系統時間)
接收方:
保存兩個隊列,audio/video分別用來存放還未播放的音頻和視頻
1。當每接收到音頻幀的時候,遍歷此時的video隊列,將此音頻幀的時間戳跟每一個視頻幀的時間戳進行比較:
1)若是音頻幀的時間在這個視頻幀的前面,幀播放該音頻
2)若是音頻跟視頻的時間戳相差在某個能夠接受的偏差內,則同時播放該音頻/視頻(並將視頻幀從video隊列中刪除)
3)若是視頻時間在前,則播放視頻幀(並將視頻幀從video隊列中刪除)
若是video隊列中的最後一幀的時間都在這個audio幀以前,在此時會把整個video隊列中的幀播放完,此時video隊列將爲空,那麼將這個音頻放入audio隊列。
2.對接收到視頻幀的時候,也作相似的處理。
同步的意思是,保證一個程序在被不適宜的切換時,不會出現問題。
對Window3.1來說,雖然有多任務,可是沒有同步基層。由於這些多任務的協做是經過調用API函數,好比(GetMessage和 PeekMessage)來實現。若是一個程序調用了GetMessage或PeekMessage,則意思就是說,我如今處於可中斷狀態。
Win32程序沒有這樣的協做多任務,他們必須作好隨時被CPU切換掉的準備。一個真正的Win32程序不該該耗盡CPU時間去等待某些事情的發生。
Win32API有四個主要的同步對象:(1)Event 事件;(2)Semaphore 信號量;(3)Mutexes 互斥;(4)Critical Section 臨界區。
除Critical Setion外,其他是系統全局對象,而且與不一樣進程及相同進程中的線程一塊兒工做,這樣同步機制也能夠用於進程同步。
1。事件(Event)
這是同步對象的一種類型類型,正如其名字含義,在這個中心周圍是一些發生在另外一個進程或線程中的特殊活動。當你但願線程暫時掛起時,不會消 耗CPU的工做週期。事件相似咱們經常使用的消息概念。若是咱們剖析消息的內核,確定能發現,它就是用事件來實現的。這裏解釋一下事件與消息的區別:
事件實際上就是消息的到達,也就是說一個消息通過一系列的過程到達了,它就會觸發一個事件處理器,事件處理器就會調用你寫的事件處理函數。 而消息就是發消息給系統,系統會有消息隊列。而後系統根據必定的調度會取到等待處理的消息(固然有可能丟失)來調用消息相應函數。雖然效果同樣,可是事件 系統顯然跟安全,由於不會丟失消息。
程序可用CreateEvent或OpenEvent對事件得到一個句柄:
HANDLE CreateEvent (
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
BOOL bManualReset, // reset type
BOOL bInitialState, // initial state
LPCTSTR lpName // object name
);
HANDLE OpenEvent(
DWORD dwDesiredAccess, // access
BOOL bInheritHandle, // inheritance option
LPCTSTR lpName // object name
);
函數參數和返回值解釋請參考MSDN,如下相同。
而後,該程序再調用WaitForSingleObject,選定事件句柄和等待超時時間。那麼線程就會被掛起,一直到其餘線程調用下面API函數,給出事件有關信號後纔再次被激活。
BOOL SetEvent(
HANDLE hEvent // handle to event
);
BOOL PulseEvent(
HANDLE hEvent // handle to event object
);
好比,當一個線程要使用另外一個線程的排序結果時,你或許但願去使用一個事件。比較糟糕的方法是執行這個線程,並在結束時設置全局變量標誌, 另外一個線程循環檢查這個標誌是否已設置,這就浪費不少CPU時間。用事件做一樣的事情則很簡單,排序線程在結束時產生一個事件,其餘線程調用 WaitForSingleObject。這就使得線程被掛起。當排序線程完成時,調用SetEvent喚醒另外一個線程繼續執行。
除了WaitForSingleObject外,還有WaitForMultipleObjects,容許一個線程被掛起,直到知足多個Event條件。
舉例說明。
音視頻通訊過程當中,咱們用一個TCP Socket,m_hDataSock接收數據,在沒有數據到達時,接收線程會被掛起,直到有數據到達或者Socket超時,來進行相應處理,示例方法以下:
UINT RecvDataThread(LPVOID pPara)
{
WSAEVENT = WSACreateEvent();
WSAEventSelect(m_hDataSock,m_hEvent,FD_READ | FD_CLOSE);
while(!m_bThreadEnd)
{
DWORD dwWait = WSAWaitForMultipleEvents(1,&m_hEvent,FALSE,18000,FALSE);
if (WSA_WAIT_TIMEOUT == dwWait)
{
//超時處理
break;
}
if(WAIT_OBJECT_0 == dwWait)
{
WSANETWORKEVENTS netEvents;
if(SOCKET_ERROR == WSAEnumNetworkEvents(m_hDataSock,m_hEvent,&netEvents))
{
continue;
}
if((netEvents.lNetworkEvents & FD_READ) && (0 == netEvents.iErrorCode[FD_READ_BIT]))
{
//接收數據
}
else if(netEvents.lNetworkEvents & FD_CLOSE)
{
//處理通道關閉
break;
}
}
}
WSACloseEvent(m_hEvent);
_endthreadex(0);
return 0;
}
2。信號量
當須要限制訪問特殊資源或限制一段代碼到某些線程是,Semaphores很是有用。好比說,同樣資源有十個,當你須要用時,已經被其餘十我的佔用了。這樣就必須等待,直到有人不用了,歸還了資源。
在Win32編程中得到Semaphores就好像得到該資源的一次控制。
爲了利用Semaphores,一個線程調用CreateSemaphore去得到一個HANDLE給Semaphores。也就是將Semaphores與資源綁定,並初始化該Semaphores,並返回該Semaphores的句柄。
函數原型以下:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD
LONG lInitialCount, // initial count
LONG lMaximumCount, // maximum count
LPCTSTR lpName // object name
);
若是Semaphores在其餘進程中建立,能夠用OpenSemaphore去獲取其句柄。
HANDLE OpenSemaphore(
DWORD dwDesiredAccess, // access
BOOL bInheritHandle, // inheritance option
LPCTSTR lpName // object name
);
接下來固然是利用等待函數來阻塞線程。若是這個Semaphore計數大於0,這等待功能只是簡單處理Semaphores的使用數,線程 繼續執行,換句話說,若是Semaphores使用數超出最大值,則等待線程被掛起。固然也能夠利用ReleaseSemaphore來釋放資源。
BOOL ReleaseSemaphore(
HANDLE hSemaphore, // handle to semaphore
LONG lReleaseCount, // count increment amount
LPLONG lpPreviousCount // previous count
);
也就是用信號量這個對象來管理某個資源的分配與回收。
3。互斥(Mutexes)
Mutex是「mutual exclusion」的縮寫。但願一次只有一個線程去訪問一個資源或一段代碼時可使用互斥。使用方法與信號量相似。建立和釋放Mutex的函數原型以下:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // SD
BOOL bInitialOwner, // initial owner
LPCTSTR lpName // object name
);
BOOL ReleaseMutex(
HANDLE hMutex // handle to mutex
);
能夠將使用方法封裝成類,以下:
class CMutex
{
public:
CMutex(HANDLE hMutex){m_hMutex = hMutex; WaitForSingleObject(m_hMutex,INFINITE);}
virtual ~CMutex(){ReleaseMutex(m_hMutex);}
private:
HANDLE m_hMutex;
};
使用的時候首先聲明一個HANDLE m_hMutex;調用接口建立Mutex,m_hMutex = CreateMutex(NULL,FALSE,NULL);而後再任何須要互斥的代碼前構造這樣一個類就能夠了。好比,CMutex mutex(m_hMutex);
4。臨界區(Critical Sections)
臨界段至關於一個微型的互斥,只能被同一個進程中的線程使用。臨界區是爲了防止多線程同時執行一段代碼。相對其餘同步機而言,臨界區相對簡單和易用。通常先聲明一個CRITICAL_SECTION類型的全局變量,而後調用下面三個函數來使用它。
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
5。WaitForSingleObject/WaitForMultipleObjects函數
其實,線程同步,除了上面四種方法外,還可使用WaitForSingleObject/WaitForMultipleObjects函數。等待的HANDLE能夠是線程的句柄,文件的句柄等。