Ruka
17 min readAug 26, 2020

--

[Research][Report]淺讀NVIDIA Video Codec SDK

請多多指教!
NZI -2020.08.27 final edit

0. 前言:

本文嘗試以初學者報告的形式介紹 NVIDIA Video Codec SDK。
內文皆為個人結論,由於我不識專精C++,若有錯誤之處,還煩請告知,不勝感激!

1. 簡介 NVIDIA Video Codec SDK

Nvidia的官網上對於這組SDK的總結是這樣的 ”A comprehensive set of API including high-performance tools, samples and documentation for hardware accelerated video encode and decode on Windows and Linux.”

這組SDK內容主要有兩個部分:API for video encode acceleration, API for video decode acceleration.前者encode的API 稱作 NVENCODE, 後者decode的部分稱作NVDECODE 。後面我會根據其所提供的Video_Codec_SDK_10.0.26 內容,來做更清楚的介紹。

SDK裡頭很重要的一部分,在於使用了硬體加速來實現更快的decode encode 運算。其中例如decode的速度,最快可達每秒2000張以上,遠超過一般影片的60fps,因此官方提到了這個能夠做到real-time的接近zero latency編碼效率。

總結來講,這組API是提供了使用了GPU硬體加速的Video Decoder和Encoder,並且利用了Nvidia 顯卡的 CUDA 進行運算。並且讓CPU有空閒去處理其他事件。

2. SDK支援度以及Setup

根據官方資料,要執行這個SDK以及相關內容,由於SDK裡會使用到CUDA,因此需要有相對應的顯示卡以及安裝CUDA Toolkit. 以下是官方提供的表格。

https://developer.nvidia.com/cuda-gpus#compute

這裡顯示了各種顯卡的cuda計算能力,網站中有顯示更多的型號,只要在網站上的都支援CUDA的安裝,這裡只顯示了其中一小部分。

https://docs.nvidia.com/deploy/cuda-compatibility/index.html
https://docs.nvidia.com/deploy/cuda-compatibility/index.html

上方這兩個表格是你在安裝前需要安裝的GPU driver. 顯示卡的版本決定了你可以安裝的cuda toolkit上限,而顯卡的架構又決定了你可以安裝的driver的版本。

比如以我的設備為例,以下是我的設備型號:

CPU: i9–9900k
GPU: RTX2060 (Turning架構)
OS: Windows 10

因此我可以選擇452.06的driver以及CUDA 10.2,來啟動此SDK。

總結,要想開始使用NVIDIA Video Codec SDK,我們要進行以下setup:

  1. 安裝對應的顯卡驅動: https://www.nvidia.com.tw/Download/index.aspx
  2. 安裝對應CUDA Toolkit(連結為CUDA 10.2): https://developer.nvidia.com/cuda-10.2-download-archive
  3. 註冊Nvidia developer帳號並且下載NVIDIA Video Codec SDK: https://developer.nvidia.com/nvidia-video-codec-sdk/download

其他需要的部分:
1. 由於SDK裏頭使用C++,因此Windows用戶需要準備好cmake以及gcc等套件。

3. NVDECODE — Decoder

Video_Codec_SDK_10.0.26

整份SDK下載之後大致上長這樣,其中Doc資料夾裡有對於API更詳細的介紹,以及Sample裡的多種使用了API的範例程式碼。後面的章節我們會從decoder api 以及範例程式碼來做簡單的解析。除了decoder以外還有encoder的範例程式碼以及API,這裡就不多作介紹。

上圖是VIDEO DECODER PIPELINE的說明圖。主要分做三個部分:Demuxer, Video Parser, and Video Decoder. 三者可分別獨立。

使用NVDECODE API來實現Video decoder pipeline,可以分成以下步驟:

1. Create a CUDA context.  
2. Query the decode capabilities of the hardware decoder.
3. Create the decoder instance(s).
4. De-Mux the content (like .mp4). This can be done using third party software like FFMPEG.
5. Parse the video bitstream using parser provided by NVDECODE API or third-party parser such as FFmpeg.
6. Kick off the Decoding using NVDECODE API.
7. Obtain the decoded YUV for further processing.
8. Query the status of the decoded frame.
9. Depending on the decoding status, use the decoded output for further processing like rendering, inferencing, postprocessing etc. 10. If the application needs to display the output,
‣ Convert decoded YUV surface to RGBA.
‣ Map RGBA surface to DirectX or OpenGL texture.
‣ Draw texture to screen.
11. Destroy the decoder instance(s) after the completion of decoding process.
12. Destroy the CUDA context.

3. NVDECODE — API & Sample Code

有關於此API的詳細介紹,可以在Doc/NVDEC_VideoDecoder_API_ProgGuide.pdf 獲得。這個API主要由兩個header file 構成:cuviddec.h and nvcuvid.h 這兩個檔案可以在\Interface資料夾下取得。

接下來我們以\Samples\AppDecode\AppDec\AppDec.cpp 這支程式來做為範例。觀察他如何使用了API的structure來執行decode運算。

AppDec.cpp

以上是我擷取的AppDec.cpp的部分程式碼,我將把重點放在下方的code欄裡。

int main(int argc, char **argv) 
{
// line 217
DecodeMediaFile(cuContext, szInFilePath, szOutFilePath, bOutPlanar, cropRect, resizeDim);
}
void DecodeMediaFile(CUcontext cuContext, const char *szInFilePath, const char *szOutFilePath, bool bOutPlanar,
const Rect &cropRect, const Dim &resizeDim)
{
//line 64
FFmpegDemuxer demuxer(szInFilePath);
NvDecoder dec(cuContext, false, FFmpeg2NvCodecId(demuxer.GetVideoCodec()), false, false, &cropRect, &resizeDim);
//line 70
do {
demuxer.Demux(&pVideo, &nVideoBytes);
nFrameReturned = dec.Decode(pVideo, nVideoBytes);
// do some else
}while (nVideoBytes);
}

main裡,主要呼叫了程式裡的DecodeMediaFile函數,而DecodeMediaFile 又創建了兩個物件FFmpegDemuxer, NvDecoder. 這兩個class是分別定義在 FFmpegDemuxer.h 以及 NvDecoder.h 檔案裡。

FFmpegDemuxerdemuxer物件負責做Demux的作業,並且將該片段的結果交給NvDecoderdec物件進行Decode. Demux的部分大部分由FFmpeg套件提供並且只有在line: 337的地方涉及一些cuda物件與ffmpeg物件轉換的部分有用到API,因此我們先跳過。

NvDecoder.h 對於其decode物件的定義,其中特別需要注意的是以下程式碼部分:

// line 84
class NvDecoder {
//line 206
private:
//line 226
int HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat);
int HandlePictureDecode(CUVIDPICPARAMS *pPicParams);
int HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo);
int ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat);
}

這裡可以看到四個fucntion中三種在API裡定義的structure: nvcuvid.hCUVIDEOFORMAT, CUVIDPARSERDISPINFO, cuviddec.hCUVIDPICPARAMS 。而這四個function的內容則是定義NvDecoder.cpp 並且提供以下功能:

HandleVideoSequence
獲取video format
HandlePictureDecode
獲取欲解碼的圖像
HandlePictureDisplay
解碼後的圖像處理
ReconfigureDecoder
重新決定decoder

最後我們簡單看一下官方給予nvcuvid.h 以及cuviddec.h 兩者的介紹。

// line 28 nvcuvid.h/********************************************************************************************************************/
//! \file nvcuvid.h
//! NVDECODE API provides video decoding interface to NVIDIA GPU devices.
//! \date 2015-2019
//! This file contains the interface constants, structure definitions and function prototypes.
/********************************************************************************************************************/
// line 28 cuviddec.h/*****************************************************************************************************/
//! \file cuviddec.h
//! NVDECODE API provides video decoding interface to NVIDIA GPU devices.
//! \date 2015-2019
//! This file contains constants, structure definitions and function prototypes used for decoding.
/*****************************************************************************************************/

API的兩份文件都是針對了常用的內容常量,結構和函數原型進行了通用的定義,並提供給各個cpp檔案一個標準的溝通格式。

4. FFmpeg with Video Codec SDK

https://developer.nvidia.com/ffmpeg

Nvidia官方提供了使用了Video Codec SDK的FFmpeg版本,使得開發者可以用常規的FFmpeg操作來進行程式開發。使用了Video Codec SDK的FFmpeg支持了以下項目:

  • Hardware-accelerated encoding of H.264 and HEVC
  • Hardware-accelerated decoding of H.264, HEVC, VP9, VP8, MPEG2, and MPEG4
  • Granular control over encoding settings such as encoding preset, rate control and other video quality parameters
  • Create high-performance end-to-end hardware-accelerated video processing, 1:N encoding and 1:N transcoding pipeline using built-in filters in FFmpeg
  • Ability to add your own custom high-performance CUDA filters using the shared CUDA context implementation in FFmpeg
  • Windows/Linux support

上圖為官方提供的步驟,根據上方的步驟可以加FFmpeg的原始碼配合SDK重新編譯成可執行檔,並啟用帶有硬體加速的ffmpeg功能。

上圖說明了FFmpeg在各種顯示卡架構下的對應版本,並且針對各種編碼格式進行了分類。
結論:使用經過Video Codec SDK擴充後的FFmpeg,可以實現啟用了硬體加速的decode、encode,streaming等項目。

5. 補充:關於視訊編解碼器(Video codec)

視訊編解碼器是用於對數位影片進行壓縮或者解壓縮的程式或者裝置。現在常見的編碼格式,如h.264,h.265,vp9等等。

  1. H.264

H.264 又稱為MPEG-4 第十部分,或"進階視訊編碼"(MPEG-4 AVC),是一種面向塊的基於運動補償的編解碼器標準。是目前視訊錄製、壓縮和發布的最常用格式之一。

H.264/AVC所提供的特性,主要有以下幾點:

  • 多參考影格的運動補償,對某些類型的場景序列,例如快速重複的閃光,反覆的剪下或者背景遮擋的情況,它能很顯著的降低編碼的位元速率。
  • 減少混疊(Aliasing)並得到更銳化的圖像。
  • 加權的運動預測,如淡入、淡出、淡出而後淡入等情況提供相當大的編碼增益。
  • 數據分割區(DP for Data partitioning),能夠將重要程度不同的語法元素分開打包傳輸,增強視訊碼流對抗信道誤碼/丟包的強韌性。

H.264/AVC被用於以下常見的部分:

  • HD DVD以及Blu-ray Disc
  • 數位電視

2. H.265 (HEVC)

H.265又稱為MPEG-H第二部分,或"高效率視訊編碼"(HEVC),被視為是H.264的繼任者。HEVC不僅提昇影像品質,同時也能達到H.264/MPEG-4 AVC兩倍之壓縮率,並提供最高到8192×4320的8K解析度。

H.265 (HEVC)所提供的特性,主要有以下幾點:

  • 編碼樹單元(Coding Tree Block),可向下分割編碼單元(Coding Unit,CU)、預測單元(Prediction Unit,PU)及轉換單元(Transform Unit,TU)。
  • 影格內編碼(Intra Coding),將預測的方向性增加到33種,並加入平面預測。
  • 去區塊濾波器(Deblocking Filter),從H.264 的一個4×4採樣網格提升至使用一個8×8個採樣網格,並採用有利於平行處理(多執行緒)的濾波方法。
  • 取樣自適應偏移(Sample Adaptive Offset),簡稱SAO濾波器,設計來以提高圖像品質,並減少振盪效應。

H.265 (HEVC)被用於以下常見方面:

  • 4K藍光光碟
  • 4K影片

3. VP9

VP9是Google公司為了替換老舊的VP8影像編碼格式並與動態專家圖像組(MPEG)主導的H.265/HEVC競爭所開發的免費、開源的影像編碼格式。VP9一般與Opus音訊編碼一起以WebM格式封裝,相比於高效率影片編碼,許多瀏覽器都支援VP9影片格式。常見的副檔名如mkv,webm.

VP9所提供的特性,主要有以下幾點:

  • 基於塊的變換編碼格式,相比HEVC更於簡潔。
  • 支援 HTML5
  • royalty-free

VP9被用於以下常見方面:

  • YouTube
  • 各大瀏覽器:Chromium、Safari、Firefox

6. Reference

--

--