1. 程式人生 > >音視訊標準:MEPG陣營(x264,x265等)和Google陣營(vp8,vp9等),中國標準(XAVS2)

音視訊標準:MEPG陣營(x264,x265等)和Google陣營(vp8,vp9等),中國標準(XAVS2)

  視訊標準:MEPG陣營和Google開源陣營。
  MPEG-2,VC1,H.263,H.264/AVC,H.265/HEVC,VP9,AV1——所有這些標準都建立在基於塊的混合視訊編碼結構上。
  通用視訊編碼標準(VVC,Versatile Video Coding)。
  FFmpeg自帶了H264、AAC、MP3的解碼器,但卻沒有(或沒有好的)相應的編碼器。相應的編碼器需要使用第三方庫。推薦使用的第三方庫為x264(H264編碼) 、FDK_AAC(AAC編碼),lame(MP3編碼)。LAME-MP3編碼引擎(音訊)。

https://img-blog.csdn.net/20180917170809629?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NoYXJlVXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

-- 直播中可用的音視訊資料涉及技術或協議:編碼方式:CBR、VBR編碼格式
視訊:H.265、H.264、MPEG-4等,封裝容器有TS、MKV、AVI、MP4等
音訊:G.711μ、AAC、Opus等,封裝有MP3、OGG、AAC等

常見的音訊壓縮格式有:MP3、AAC、OGG、WMA、Opus、FLAC、APE、m4a和AMR等。
常見的視訊封裝格式有:MP4、3GP、AVI、MKV、WMV、MPG、VOB、FLV、SWF、MOV、RMVB和WebM等。
Android錄音支援的格式有amr、aac

> 中國的音視訊標準
中國XAVS2實時編碼器開源地址- https://github.com/pkuvcl/xavs2
中國XAVS2實時編碼器開源地址- https://gitee.com/pkuvcl/xavs2

> Google開源陣營
-- YUV 處理庫
 https://chromium.googlesource.com/libyuv/libyuv/

,Google 開源的一個 YUV 處理庫,上面只針對 1080p->540p 視訊幀縮放的演算法,而對於通用的壓縮處理,可以直接使用這裡的實現,對比起 FFmpeg 的速度快上不少。

-- VP8 的開源實現:libvpx:
  libvpx 是 VP8 的唯一開源實現,由 On2 Technologies 開發,Google 收購後將其開放原始碼,License 非常寬鬆可以自由使用。
編碼器的選擇之VP8:
  VP8 是一個開放的視訊壓縮格式,最早由 On2 Technologies 開發,隨後由 Google 釋出。同時 Google 也釋出了 VP8 編碼的實做庫:libvpx,以 BSD 授權條款的方式發行,隨後也附加了專利使用權。而在經過一些爭論之後,最終 VP8 的授權確認為一個開放原始碼授權。
  目前支援 VP8 的網頁瀏覽器有 Opera、Firefox 和 Chrome。

-- VP9 的開源實現:ibvpx:
  libvpx 是 VP9 的唯一開源實現,由 Google 開發維護,裡面有部分程式碼是 VP8 和 VP9 公用的,其餘分別是 VP8 和 VP9 的編解碼實現。
  vpx則是Google推出的開源視訊編碼器,它提出的VP9編碼標準的效能也不錯。

  Google已在幾年前已經決定Chrome瀏覽裡放棄H.264標準的支援,轉而只支援自家的VP8(最新版是VP9)編碼格式。而且,2011年被Google收購的著名實時音視訊方案GIPS——已被Google開源多年並取名為WebRTC,WebRTC中的實時音視訊編碼預設使用的就是VP8標準。
  H.264(下一代是H.265)和VP8(下一代是VP9)編碼兩種:
1.採用 H.264 視訊編碼和 AAC 音訊編碼的 MP4 檔案(H.264/AAC/MP4 組合);
2.採用 VP8 視訊編碼和 Vorbis 音訊編碼的 WebM 檔案(VP8/Vorbis/WebM 組合);


  通常「H.264」指代 H.264/AAC/MP4 這個組合,而「WebM」指代 VP8/Vorbis/WebM 這個組合。
  2010 年中的時候 Google 宣佈將 VP8 永久免費。Google 又基於開源容器格式 Matroska 開發了 WebM 容器格式,用以封裝 VP8 編碼的視訊和 Vorbis 編碼的音訊。隨後 Google 連同 Mozilla 和 Opera,準備將 VP8/Vorbis/WebM (統稱為 WebM)推廣為網路視訊的通用格式。Google Chrome 瀏覽器在 WebM 釋出後迅速更新為同時支援 Theora、H.264、WebM 三種格式。Mozilla 和 Opera 也宣佈將在其瀏覽器的後續版本(主要是 Firefox 4)中原生支援 WebM。
  MPEG LA 將 H.264 專利許可分為兩種:H.264 編碼器和解碼器(不論軟硬體)廠商需要購買一種 H.264 的專利許可協議,H.264 編碼的視訊的分發商(如電視臺等)需要購買另外一種 H.264 的專利許可協議。

> MEPG陣營
-- x264
openh264思科開源的另外一個h264編碼器- https://github.com/cisco/openh264
官網x264編碼的原始碼- http://www.videolan.org/developers/x264.html
Android NDK 編譯 FFmpeg + x264 + fdk-aac (arm/x86)的配置指令碼- http://blog.csdn.net/panda1234lee/article/details/53099203
x264的開源庫在android中的呼叫方法-- http://www.yanfaw.com/technology/201108/16/435.html

 最簡單的視訊編碼器:編譯(libx264,libx265,libvpx)- http://blog.csdn.net/leixiaohua1020/article/details/42069383
x264,x265,vpx這三個開源的視訊編碼器可以說是當今“最火”的視訊編碼器。x264現在佔據著H.264視訊編碼器的半壁江山;x265則是目前實現H.265標準最好的開源視訊編碼器,並且在未來可能接替x264;而vpx則是Google推出的開源視訊編碼器,它提出的VP9編碼標準的效能也不錯。
 視訊轉碼技術就不得不提到兩個開源工程:ffmpeg和x264。

-- h264硬編解碼(Android)
從技術下游(SDK 使用方)跑到了技術上游(SDK 提供方)。
H264編碼片,I/P/B/SP/SI幀;普通視訊的I/P/B幀。I幀內預測,P幀間預測,B雙向預測幀。

-- OpenH264;x264
H.264 的開源實現:OpenH264;x264
  OpenH264 是思科實現的開源 H.264 編碼,專案在 2013 年開源,雖然 H.264 需要交納不菲的專利費用,但是專利費有一個年度上限,思科把 OpenH264 實現的年度專利費交滿後,另外,firefox 直接內建了 OpenH264,作為其在 WebRTC 中的視訊編解碼器使用。OpenH264 事實上就可以免費自由的使用了。
  x264 是一個採用 GPL 授權的視訊編碼自由軟體。x264 的主要功能在於進行 H.264/MPEG-4 AVC 的視訊編碼,而不是作為解碼器(decoder)之用。

除去費用問題比較來看:
OpenH264 CPU 的佔用相對 x264低很多;OpenH264 只支援 baseline profile,x264 支援更多 profile。

-- H.265 的開源實現:libde265、x265:
  libde265 HEVC 由 struktur 公司以開源許可證 GNU LesserGeneral Public License (LGPL) 提供,觀眾可以較慢的網速下欣賞到最高品質的影像。跟以前基於H.264標準的解碼器相比,libde265 HEVC 解碼器可以將您的全高清內容帶給多達兩倍的受眾,或者,減少 50%流媒體播放所需要的頻寬。高清或者 4K/8K 超高清流媒體播放,低延遲/低頻寬視訊會議,以及完整的移動裝置覆蓋。具有「擁塞感知」視訊編碼的穩定性,十分適合應用在 3/4G 和 LTE 網路。
  x265 是由 MulticoreWare 開發,並開源。採用 GPL 協議,但是資助這個專案的幾個公司組成了聯盟可以在非 GPL 協議下使用這個軟體。

-- FFmpeg
  FFmpeg 是一個自由軟體,可以執行音訊和視訊多種格式的錄影、轉換、流功能,包含了 libavcodec -這是一個用於多個專案中音訊和視訊的解碼器庫,以及 libavformat -一個音訊與視訊格式轉換庫。
  FFmpeg 這個單詞中的 FF 指的是 Fast Forward。這個專案最初是由 Fabrice Bellard 發起的,而現在是由 Michael Niedermayer 在進行維護。許多 FFmpeg 的開發者同時也是 MPlayer 專案的成員,FFmpeg 在 MPlayer 專案中是被設計為伺服器版本進行開發。

  所謂容器,就是把編碼器生成的多媒體內容(視訊,音訊,字幕,章節資訊等)混合封裝在一起的標準。容器使得不同多媒體內容同步播放變得很簡單,而容器的另一個作用就是為多媒體內容提供索引,也就是說如果沒有容器存在的話一部影片你只能從一開始看到最後,不能拖動進度條(當然這種情況下有的播放器會話比較長的時間臨時建立索引),而且如果你不自己去手動另外載入音訊就沒有聲音。
  我們在流媒體傳輸,尤其是直播中主要採用的就是 FLV 和 MPEG2-TS 格式,分別用於 RTMP/HTTP-FLV 和 HLS 協議。
  實時音視訊通訊 = 音視訊處理 + 網路傳輸。包括採集、編碼、網路傳輸、解碼、播放等環節。音視訊處理中最為關鍵的視訊編解碼是個頭等重要的問題,對於開發者來說,以目前所能找到的技術資源以及應用的普及程度,因為背靠巨頭,H.264(最新版是H.265,微軟和蘋果一直都在背後力推)和VP8(最新版是VP9,由Google力推)是最為主流的兩種編碼。

-- 實時視訊傳輸的關鍵技術 H.264 全解析- http://blog.csdn.net/Byeweiyang/article/details/78134674
  H.264 碼流傳輸的基本單元是 NAL 單元,NAL 單元內攜帶的最關鍵的資料是引數集和片資料;解碼的基本單元是巨集塊,解碼器根據預測資訊和殘差資料,解碼出原始資料;巨集塊解碼之後拼接成片,片拼接成影象,而一幅幅影象則構成了視訊!
  編碼基本框架:預測編碼、變換編碼、熵編碼。
  無論編碼器的結構如何,相應的視訊編碼的控制都是編碼器實現的核心問題。在編碼過程中,並沒有直接控制編碼資料大小的方式,只能通過調整量化過程的量化引數 QP 值間接控制,而由於 QP 和編碼資料大小並沒有確定的關係,所以編碼器的位元速率控制無法做到很精細,基本都靠試。要麼是中途改變後續巨集塊的質量,要麼是重新編碼改變所有巨集塊的質量。

  解碼過程就是編碼的逆過程:熵解碼、變換解碼、預測解碼。
  以巨集塊為單位,依次進行熵解碼、反量化、反變換,得到殘差資料,再結合巨集塊裡面的預測資訊,找到已解碼的被參考塊,進而結合已解碼被參考塊和本塊殘差資料,得到本塊的實際資料。巨集塊解碼後,組合出片,片再組合出影象。

-- 可伸縮編碼(Scalable Video Coding, SVC)實質上是將視訊資訊按照重要性分解,對分解的各個部分按照其自身的統計特性進行編碼。一般它會將視訊編碼為一個基本層和一組增強層。基本層包含基本資訊,可以獨立解碼,增強層依賴於基本層,可以對基本層的資訊進行增強,增強層越多,視訊資訊的恢復質量也就越高。
 SVC 通常有三種:
 1.空域可伸縮:可以解碼出多種解析度的視訊;
 2.時域可伸縮:可以解碼出多種幀率的視訊,解析度相同;
 3.質量可伸縮:可以解碼出多種位元速率的視訊,解析度、幀率相同;

> android MediaCodec 實現h264硬編解碼全過程- http://download.csdn.net/detail/b_xjie/8744773
MediaCodec 實現h264硬編解碼全過程,視訊資料(onPreviewFrame)從攝像頭讀出 yv12格式,轉換為I420,投遞給encoder,再從encoder取出編碼後的h264資料投遞給decoder後顯示到surfaceView; 實現了udp將h264資料傳送到指定主機,可通過vlc播放; 備有可以讀取本地264檔案流投遞給解碼器播放; 小米 4.4.2 測試通過.
   /**
     * H264編碼
     *
     * @param input
     * @param output
     * @return
     */
    private int offerEncoder(byte[] input, byte[] output) {
        int pos = 0;
        try {
            ByteBuffer[] inputBuffers = mMediaEncoder.getInputBuffers();
            ByteBuffer[] outputBuffers = mMediaEncoder.getOutputBuffers();
            int inputBufferIndex = mMediaEncoder.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                Log.d(TAG, "offerEncoder InputBufSize: " + inputBuffer.capacity() + " inputSize: " + input.length + " bytes");
                inputBuffer.clear();
                inputBuffer.put(input);
                mMediaEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);

            }
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mMediaEncoder.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];

                byte[] data = new byte[bufferInfo.size];
                outputBuffer.get(data);

                Log.d(TAG, "offerEncoder InputBufSize:" + outputBuffer.capacity() + " outputSize:" + data.length + " bytes written");

                if (mMediaHead != null) {
                    System.arraycopy(data, 0, output, pos, data.length);
                    pos += data.length;
                } else // 儲存pps sps 只有開始時 第一個幀裡有, 儲存起來後面用
                {
                    Log.d(TAG, "offer Encoder save sps head,len:" + data.length);
                    ByteBuffer spsPpsBuffer = ByteBuffer.wrap(data);
                    if (spsPpsBuffer.getInt() == 0x00000001) {
                        mMediaHead = new byte[data.length];
                        System.arraycopy(data, 0, mMediaHead, 0, data.length);
                    } else {
                        Log.e(TAG, "not found media head.");
                        return -1;
                    }
                }
                mMediaEncoder.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mMediaEncoder.dequeueOutputBuffer(bufferInfo, 0);

            }

            if (output[4] == 0x65) { // key frame 編碼器生成關鍵幀時只有 00 00 00 01 65 沒有pps
                // sps, 要加上
                System.arraycopy(output, 0, input, 0, pos);
                System.arraycopy(mMediaHead, 0, output, 0, mMediaHead.length);
                System.arraycopy(input, 0, output, mMediaHead.length, pos);
                pos += mMediaHead.length;
            }

        } catch (Throwable t) {
            t.printStackTrace();
        }
        return pos;
    }

    /**
     * H264解碼
     *
     * @param input
     * @param length
     */
    private void offerDecoder(byte[] input, int length) {
        try {
            ByteBuffer[] inputBuffers = mMediaDecoder.getInputBuffers();
            int inputBufferIndex = mMediaDecoder.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                long timestamp = mFrameIndex++ * 1000000 / FRAME_RATE;
                Log.d(TAG, "offerDecoder timestamp: " + timestamp + " inputSize: " + length + " bytes");
                inputBuffer.clear();
                inputBuffer.put(input, 0, length);
                mMediaDecoder.queueInputBuffer(inputBufferIndex, 0, length, timestamp, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mMediaDecoder.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                Log.d(TAG, "offerDecoder OutputBufSize:" + bufferInfo.size + " bytes written");

        // If a valid surface was specified when configuring the codec,
        // passing true renders this output buffer to the surface.
                mMediaDecoder.releaseOutputBuffer(outputBufferIndex, true);
                outputBufferIndex = mMediaDecoder.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }