1. 程式人生 > >Android 音視訊錄製(3)——全關鍵幀視訊錄製(視訊編輯必備)

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  
版權宣告:本文為博主原創文章,轉載請附上博文連結!