最近用javacv的ffmpeg包的ffmpegframegrabber幀捕捉器對捕捉到的音頻幀和視頻幀做了同步的播放。采用的同步方法是視頻向音頻同步。
具體的思路如下:
(1)首先介紹ffmpeg是如何捕捉視頻文件的圖像和聲音的
1
|
ffmpegframegrabber fg = new ffmpegframegrabber("a video file path or a url); |
得到幀捕捉器對象后,調用它的grab()方法就會返回捕捉到的frame對象。這個frame可以是視頻幀或者是音頻幀,這是因為音視頻幀時按照時間戳在播放時間先上排列的。當然捕捉到的幀都是已經譯碼過的,并且存儲在java.nio.buffer對象中,對于視頻幀,buffer是儲存圖像的像素數據比如rgb,然后通過
1
|
bufferedimage bi = ( new java2dframeconverter()).getbufferedimage(f); |
就可以得到圖片,得到的圖片可以進行一系列的處理或者不處理直接顯示在swing組件上。對應音頻幀,buffer是儲存音頻的pcm數據,這個pcm可以是float或者short的,然后用java.sounds.sample里面的sourcedataline.write方法就可以將這些音頻pcm數據寫入到揚聲器中。
(2)接著介紹如何不斷得將得到的幀播放出來。首先是單獨播放視頻:
1
2
3
4
5
6
7
|
while ( true ) { frame f = fg.grab(); if (f.image!= null ) label.seticon( new imageicon(( new java2dframeconverter()).getbufferedimage(f))); thread.sleep( 1000 /視頻幀率); } |
單獨播放音頻同理,將數據寫入到聲卡即可。例子
(3)生產消費者模式。
上圖是程序實現的方法,采用生產者模式將捕獲到的幀進行判斷,如果是視頻幀就生產到視頻fifo中,如果是音頻幀就生產到音頻fifo中,然后音頻播放線程和視頻播放線程分別從各自的幀倉庫消費里面的幀。之所以采用生產消費者模式是因為幀捕獲的速度是大于幀的消耗的,所以我們優先捕獲幀來緩沖,或者進一步對捕獲的幀進行預處理,而視頻和音頻播放線程只需要將處理過的幀直接播放顯示即可。
(4)實現音視頻同步的方法:播放兩幀音頻里面的所有視頻幀。
想要實現音視頻同步,必須要有幀的時間戳,這里捕獲到的幀只有播放的時間戳pts,沒有譯碼時間戳dts,所以我們只需要根據播放時間戳來決定播放即可。
程序的實現是根據上圖來的, 當音頻線程開始播放音頻幀a1時,就調用視頻線程的setrun方法,并且傳遞當前要播放的音頻幀時間戳curtime和下一幀音頻幀a2的時間戳nexttime給處于wait態的視頻線程,然后視頻線程啟動,開始從視頻fifo中取出視頻幀g1,然后計算g1和a1的時間差,作為播放的延時,thread.sleep(t1)后,視頻線程就將圖片顯示在swing組件上,比如jlabel.seticon(image)。然后視頻線程再取出一幀圖像g2,比較g2的時間戳和a2的時間戳,如果g2時間戳小于a2,那么視頻線程繼續延時t2以后,播放這個g2圖像,接著g3同理,直到取得g4,和a2比較發現g4時間戳大于a2,那么視頻線程就進入wait態,等待下一次啟動。然后音頻線程播放完a1音頻幀以后,就從倉庫取出音頻幀a3,然后將a2的時間戳和a3的時間戳傳遞給視頻線程,然后開始播放a2,然后堵塞的視頻線程同理繼續播放。
(5)動態調節延時時間
由于個人pc都不是實時操作系統,也就是thread.sleep是不精確的,并且受到聲卡播放聲音的制約,所以上面的基本實現思路是需要加以完善的。首先java的sourcedataline的方法是依照一定的速度從內部緩沖區取出音頻線程寫入的數據,如果音頻寫入的數據被取光了,那么音頻播放就會發生卡頓,但是如果一次音頻數據寫入過多,那么就會發生音視頻可能就會不同步,所以要確保sourcedataline的內部緩沖區是留有一定數據的,否則就會造成卡頓,但是數據量又不能過多,所以我們在g3到a2這段時間來進行聲音播放的調節,由于延時的不精準性,寫入的a1幀的數據可能時間還沒滿t6就可能被聲卡取光了,所以在播放完g3圖像以后,聲音線程會判斷根據sourcedataline.available()返回的數據量進行判斷,如果數據量快要完了,就減少g3到a2的延時時間t4。這樣子就可以保證數據量是不會變為0造成聲音卡頓。
(6)下面是程序在window64下測試和ubuntu14下測試的結果圖: 播放是比較流暢的,同步也是可以的,但是開著播放比企鵝在ide如idea中寫代碼的話,會卡,畢竟idea也是用java開發的,所以idea的運行會影響其他java程序,但是其他進程不會影響。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/A694543965/article/details/78317479