Android 音視訊錄製(3)——全關鍵幀視訊錄製(視訊編輯必備)
全關鍵幀錄製顧名思義,就是視訊所有幀都是關鍵幀(I幀),毫無疑問,全I幀的視訊肯定會比正常錄製的視訊要大很多,但是為什麼需要全I幀錄製的視訊?原因就是,大部分音視訊app錄製完視訊之後都要對視訊進行編輯吧,但是如果不是全I幀錄製的視訊檔案,編輯起來會非常困難,而全I幀視訊肯定很容易編輯。比如你要做一個時光倒流的功能,或者你要對視訊加特效,哪怕最簡單的,視訊播放拖動進度(精確seek而非關鍵幀seek),全I幀視訊肯定比普通視訊流暢很多很多。所以全關鍵幀錄製對於錄製後的視訊編輯是非常重要的。
那根本原因是什麼?
我舉一個例子,視訊的seek操作:首先要知道視訊的seek分兩種,一種是精確seek,一種是關鍵幀seek(目前絕大部分的播放器都是關鍵幀seek)。
假設一個視訊是3秒,視訊幀為10幀/秒,關鍵幀間隔為1秒,那麼這個視訊第1幀為關鍵幀,第11幀為關鍵幀,第21幀為關鍵幀,比如我現在需要做一個seek操作,seek到1.3秒,如果採用的是關鍵幀seek,它會seek到第11幀(也有可能是第21幀,這個是看播放器是根據哪個原則來seek,一般有這三種關鍵幀seek原則,向下取關鍵幀/向上取關鍵幀/最近關鍵幀),而精確seek的話,是seek到13幀,這就是關鍵幀seek和精確seek的區別。但涉及到視訊編輯的app無一例外,精確seek才是最符合產品設計的。
所以如果我對於非全關鍵幀的視訊我要seek到1.3秒位置,應該如何做?
首先去到最近的關鍵幀,第11幀,然後根據H264的編碼原理,一直根據這個關鍵幀解碼到第13幀,這個過程非常非常耗時,無論你採用多麼高階的解碼器,多麼高深的演算法,這個過程都幾乎不可優化。但是如果你這個視訊是全關鍵幀錄製的就不同了,直接seek到第13幀,用不著任何的解碼,所以這個過程會非常的流暢。尤其遇到這樣的產品需求的時候:拖動進度條,視訊畫面隨進度滾動。如果產品需要精確seek,而且視訊又不是全關鍵幀,這個時候就GG了。
所以很多的視訊編輯都會設計到視訊幀的重編解碼,採用關鍵幀,會使這個重編解碼的過程高效很多。
說了那麼多,那麼我下面就說說怎樣錄製全關鍵幀視訊。再次說明,要看此篇文章,請務必先檢視上面說的三篇文章。
全關鍵幀錄製——buffer錄製
1,配置關鍵幀間隔為0
在buffer錄製中,我在編碼器的prepared函式中有這樣的一段程式碼:首先配置編碼器的時候需要配置關鍵幀間隔為0
if(!mIsAllKeyFrame) {
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); //單位是 秒
}else{
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);//設定為0
}
1
2
3
4
5
2,輸入buffer的時候請求關鍵幀
在編碼器的input()buffer時候,需要請求關鍵幀,需要在dequeueInputBuffer操作之前
if(mIsAllKeyFrame){
requestKeyFrame();
}
1
2
3
請求關鍵幀程式碼:
@TargetApi(19)
protected void requestKeyFrame() {
if (mIsAllKeyFrame){
try {
Bundle reqKeyCmd = new Bundle();
reqKeyCmd.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
mMediaCodec.setParameters(reqKeyCmd);
} catch (Exception e) {
}
}
}
1
2
3
4
5
6
7
8
9
10
11
3,輸出編碼器buffer的時候也要請求關鍵幀
請求關鍵幀操作需要在dequeueOutputBuffer之前
if(mIsAllKeyFrame){
requestKeyFrame();
}
1
2
3
這樣子我們得到的視訊就是關鍵幀視訊了。
全關鍵幀視訊錄製——Surface錄製
1,Surface錄製的時候也一樣,配置編碼器的時候設定關鍵幀間隔為0
if(!mIsAllKeyFrame) {
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
}else{
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);//設定全關鍵幀
}
1
2
3
4
5
2,EGL繪製前需要請求關鍵幀,繪製後也需要請求關鍵幀(這裡不同於buffer錄製)
//egl 繪製
public void render(float[] surfaceTextureMatrix, float[] mvpMatrix) {
if(isAllKeyFrame()){
requestKeyFrame();
}
mRenderer.draw(surfaceTextureMatrix, mvpMatrix);
if(isAllKeyFrame()){
requestKeyFrame();
}
}
1
2
3
4
5
6
7
8
9
10
3,同樣是在輸出的時候需要請求關鍵幀,這個操作和buffer錄製一樣,都需要在dequeueOutputBuffer之前操作。
@Override
public void output(boolean isEos) {
if(isAllKeyFrame()){
requestKeyFrame();
}
super.output(isEos);
}
1
2
3
4
5
6
7
至此,關鍵幀視訊錄製的已經講完了,歡迎小夥伴們的留言!
---------------------
作者:白雲蒼狗日悠悠
來源:CSDN
原文:https://blog.csdn.net/DaltSoftware/article/details/78721124
版權宣告:本文為博主原創文章,轉載請附上博文連結!