4.5.2. 記憶體到記憶體有狀態影片編碼器介面

有狀態影片編碼器接收顯示順序的原始影片幀,並將其編碼成位元組流。它生成完整的位元組流塊,包括所有元資料、頭資訊等。生成的位元組流不需要客戶端進行任何進一步的後處理。

強烈不推薦在驅動中執行軟體流處理、頭生成等操作來支援此介面。如果需要此類操作,強烈建議使用無狀態影片編碼器介面(開發中)。

4.5.2.1. 文件中使用的約定和符號

  1. 除非本文另有說明,否則通用 V4L2 API 規則適用。

  2. “必須”、“可以”、“應該”等詞語的含義與 RFC 2119 中所述一致。

  3. 所有未標記為“可選”的步驟都是必需的。

  4. VIDIOC_G_EXT_CTRLS()VIDIOC_S_EXT_CTRLS() 可以與 VIDIOC_G_CTRL()VIDIOC_S_CTRL() 互換使用,除非另有說明。

  5. 單平面 API(參見單平面和多平面 API)及適用結構體可以與多平面 API 互換使用,除非另有說明,具體取決於編碼器能力並遵循通用 V4L2 指南。

  6. i = [a..b]:從 a 到 b(含)的整數序列,即 i = [0..2]:i = 0, 1, 2。

  7. 給定一個 OUTPUT 緩衝區 A,則 A' 代表 CAPTURE 佇列上的一個緩衝區,其中包含處理緩衝區 A 後產生的資料。

4.5.2.2. 術語表

請參見術語表

4.5.2.3. 狀態機

DOT digraph of encoder state machine

編碼器狀態機

4.5.2.4. 查詢能力

  1. 要列舉編碼器支援的編碼格式集合,客戶端可以對 CAPTURE 呼叫 VIDIOC_ENUM_FMT()

    • 將返回支援的完整格式集合,而不論在 OUTPUT 上設定的格式是什麼。

  2. 要列舉支援的原始格式集合,客戶端可以對 OUTPUT 呼叫 VIDIOC_ENUM_FMT()

    • 只會返回 CAPTURE 上當前啟用的格式所支援的格式。

    • 為了列舉給定編碼格式支援的原始格式,客戶端必須首先在 CAPTURE 上設定該編碼格式,然後列舉 OUTPUT 上的格式。

  3. 客戶端可以使用 VIDIOC_ENUM_FRAMESIZES() 來檢測給定格式支援的解析度,透過在 v4l2_frmsizeenum pixel_format 中傳入所需的畫素格式。

    • VIDIOC_ENUM_FRAMESIZES() 為編碼畫素格式返回的值將包括編碼器對給定編碼畫素格式支援的所有可能的編碼解析度。

    • VIDIOC_ENUM_FRAMESIZES() 為原始畫素格式返回的值將包括編碼器對給定原始畫素格式以及 CAPTURE 上當前設定的編碼格式所支援的所有可能的幀緩衝區解析度。

  4. 客戶端可以使用 VIDIOC_ENUM_FRAMEINTERVALS() 來檢測給定格式和解析度支援的幀間隔,透過在 v4l2_frmivalenum pixel_format 中傳入所需的畫素格式,以及在 v4l2_frmivalenum widthv4l2_frmivalenum height 中傳入解析度。

    • VIDIOC_ENUM_FRAMEINTERVALS() 為編碼畫素格式和編碼解析度返回的值將包括編碼器對給定編碼畫素格式和解析度支援的所有可能的幀間隔。

    • VIDIOC_ENUM_FRAMEINTERVALS() 為原始畫素格式和解析度返回的值將包括編碼器對給定原始畫素格式和解析度以及 CAPTURE 上當前設定的編碼格式、編碼解析度和編碼幀間隔所支援的所有可能的幀間隔。

    • VIDIOC_ENUM_FRAMEINTERVALS() 的支援是可選的。如果未實現,則除了編解碼器本身的限制外,沒有特殊限制。

  5. 如果適用,可以透過 VIDIOC_QUERYCTRL() 使用各自的控制元件查詢 CAPTURE 上當前設定的編碼格式所支援的配置檔案和級別。

  6. 可以透過查詢各自的控制元件來發現任何其他編碼器能力。

4.5.2.5. 初始化

  1. 透過 VIDIOC_S_FMT()CAPTURE 佇列上設定編碼格式。

    • 必需欄位

      type

      適合 CAPTUREV4L2_BUF_TYPE_* 列舉。

      pixelformat

      要生成的編碼格式。

      sizeimage

      所需的 CAPTURE 緩衝區大小;編碼器可能會根據硬體要求進行調整。

      width, height

      忽略(只讀)。

      其他欄位

      遵循標準語義。

    • 返回欄位

      sizeimage

      調整後的 CAPTURE 緩衝區大小。

      width, height

      編碼器根據當前狀態(例如 OUTPUT 格式、選擇矩形等)選擇的編碼大小(只讀)。

    重要

    更改 CAPTURE 格式可能會更改當前設定的 OUTPUT 格式。新 OUTPUT 格式的確定方式由編碼器決定,客戶端必須確保它之後符合其需求。

  2. 可選。透過 VIDIOC_ENUM_FMT() 列舉所選編碼格式支援的 OUTPUT 格式(源的原始格式)。

    • 必需欄位

      type

      適合 OUTPUTV4L2_BUF_TYPE_* 列舉。

      其他欄位

      遵循標準語義。

    • 返回欄位

      pixelformat

      當前在 CAPTURE 佇列上選擇的編碼格式所支援的原始格式。

      其他欄位

      遵循標準語義。

  3. 透過 VIDIOC_S_FMT()OUTPUT 佇列上設定原始源格式。

    • 必需欄位

      type

      適合 OUTPUTV4L2_BUF_TYPE_* 列舉。

      pixelformat

      源的原始格式。

      width, height

      源解析度。

      其他欄位

      遵循標準語義。

    • 返回欄位

      width, height

      可能會根據當前選擇的格式的要求,以及 VIDIOC_ENUM_FRAMESIZES() 報告的資訊,進行調整以匹配編碼器最小值、最大值和對齊要求。

      其他欄位

      遵循標準語義。

    • 設定 OUTPUT 格式將根據新解析度,將選擇矩形重置為其預設值,如下一步所述。

  4. 透過 VIDIOC_S_PARM()OUTPUT 佇列上設定原始幀間隔。這也會將 CAPTURE 佇列上的編碼幀間隔設定為相同值。

    • 必需欄位

      type

      適合 OUTPUTV4L2_BUF_TYPE_* 列舉。

      parm.output

      將除 parm.output.timeperframe 之外的所有欄位設定為 0。

      parm.output.timeperframe

      所需的幀間隔;編碼器可能會根據硬體要求進行調整。

    • 返回欄位

      parm.output.timeperframe

      調整後的幀間隔。

    重要

    更改 OUTPUT 幀間隔會設定編碼器用於編碼影片的幀率。因此,將幀間隔設定為 1/24(或每秒 24 幀)將產生一個可以以該速度播放的編碼影片流。OUTPUT 佇列的幀間隔只是一個提示,應用程式可以以不同的速率提供原始幀。驅動可以使用它來幫助排程並行執行的多個編碼器。

    在下一步中,CAPTURE 幀間隔可以選擇性地更改為不同的值。這對於離線編碼很有用,因為編碼幀間隔可以與提供原始幀的速率不同。

    重要

    timeperframe 處理的是,而不是場。因此,對於隔行掃描格式,這是每兩個場的時間,因為一幀由一個頂場和一個底場組成。

    注意

    由於歷史原因,更改 OUTPUT 幀間隔也會更改 CAPTURE 佇列上的編碼幀間隔。理想情況下,這些應該是獨立的設定,但這會破壞現有 API。

  5. 可選 透過 VIDIOC_S_PARM()CAPTURE 佇列上設定編碼幀間隔。只有當編碼幀間隔與原始幀間隔不同時才需要這樣做,這通常是離線編碼的情況。對此功能的支援透過 V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL 格式標誌發出訊號。

    • 必需欄位

      type

      適合 CAPTUREV4L2_BUF_TYPE_* 列舉。

      parm.capture

      將除 parm.capture.timeperframe 之外的所有欄位設定為 0。

      parm.capture.timeperframe

      所需的編碼幀間隔;編碼器可能會根據硬體要求進行調整。

    • 返回欄位

      parm.capture.timeperframe

      調整後的幀間隔。

    重要

    更改 CAPTURE 幀間隔會設定編碼影片的幀率。它設定緩衝區到達 CAPTURE 佇列的速率,這取決於編碼器的速度以及原始幀在 OUTPUT 佇列中入隊的速度。

    重要

    timeperframe 處理的是,而不是場。因此,對於隔行掃描格式,這是每兩個場的時間,因為一幀由一個頂場和一個底場組成。

    注意

    並非所有驅動都支援此功能,在這種情況下,只需為 OUTPUT 佇列設定所需的編碼幀間隔即可。

    然而,能夠根據 OUTPUT 幀間隔排程多個編碼器的驅動必須支援此可選功能。

  6. 可選。如果希望流元資料的可見解析度與完整的 OUTPUT 解析度不同,則透過 VIDIOC_S_SELECTION()OUTPUT 佇列上設定它。

    • 必需欄位

      type

      適合 OUTPUTV4L2_BUF_TYPE_* 列舉。

      target

      設定為 V4L2_SEL_TGT_CROP

      r.left, r.top, r.width, r.height

      可見矩形;這必須適合 V4L2_SEL_TGT_CROP_BOUNDS 矩形,並可能根據編解碼器和硬體限制進行調整。

    • 返回欄位

      r.left, r.top, r.width, r.height

      編碼器調整後的可見矩形。

    • OUTPUT 上支援以下選擇目標:

      V4L2_SEL_TGT_CROP_BOUNDS

      等於完整的源幀,與啟用的 OUTPUT 格式匹配。

      V4L2_SEL_TGT_CROP_DEFAULT

      等於 V4L2_SEL_TGT_CROP_BOUNDS

      V4L2_SEL_TGT_CROP

      源緩衝區內將編碼到 CAPTURE 流中的矩形;預設為 V4L2_SEL_TGT_CROP_DEFAULT

      注意

      此選擇目標的常見用例是編碼解析度不是宏塊倍數的源影片,例如,對於具有 16x16 宏塊大小的編解碼器,常見的 1920x1080 解析度可能要求源緩衝區對齊到 1920x1088。為了避免編碼填充,客戶端需要明確將此選擇目標配置為 1920x1080。

    警告

    編碼器可能會將裁剪/合成矩形調整到最近支援的矩形,以滿足編解碼器和硬體要求。客戶端需要檢查 VIDIOC_S_SELECTION() 返回的調整後的矩形。

  7. 透過 VIDIOC_REQBUFS()OUTPUTCAPTURE 分配緩衝區。這可以按任意順序執行。

    • 必需欄位

      count

      請求分配的緩衝區數量;大於零。

      type

      適合 OUTPUTCAPTUREV4L2_BUF_TYPE_* 列舉。

      其他欄位

      遵循標準語義。

    • 返回欄位

      count

      實際分配的緩衝區數量。

    警告

    實際分配的緩衝區數量可能與給定的 count 不同。呼叫返回後,客戶端必須檢查 count 的更新值。

    注意

    為了分配多於最小數量的 OUTPUT 緩衝區(用於管道深度),客戶端可以查詢 V4L2_CID_MIN_BUFFERS_FOR_OUTPUT 控制元件以獲取所需的最小緩衝區數量,然後將獲取的值加上所需的額外緩衝區數量在 count 欄位中傳遞給 VIDIOC_REQBUFS()

    或者,可以使用 VIDIOC_CREATE_BUFS() 來更好地控制緩衝區分配。

    • 必需欄位

      count

      請求分配的緩衝區數量;大於零。

      type

      適合 OUTPUTV4L2_BUF_TYPE_* 列舉。

      其他欄位

      遵循標準語義。

    • 返回欄位

      count

      調整為已分配的緩衝區數量。

  8. 透過 VIDIOC_STREAMON() 開始在 OUTPUTCAPTURE 佇列上流式傳輸。這可以按任意順序執行。當兩個佇列都開始流式傳輸時,實際編碼過程開始。

注意

如果客戶端在編碼過程中停止 CAPTURE 佇列,然後再次重新啟動,編碼器將開始生成一個獨立於停止前生成的流。具體限制取決於編碼格式,但可能包括以下含義:

  • 重新啟動後生成的編碼幀不得引用停止前生成的任何幀,例如 H.264/HEVC 不得有長期參考幀;

  • 任何必須包含在獨立流中的標頭檔案必須再次生成,例如 H.264/HEVC 的 SPS 和 PPS。

4.5.2.6. 編碼

初始化 序列成功完成後達到此狀態。在此狀態下,客戶端透過 VIDIOC_QBUF()VIDIOC_DQBUF() 將緩衝區入隊和出隊到兩個佇列,遵循標準語義。

編碼的 CAPTURE 緩衝區的內容取決於啟用的編碼畫素格式,並可能受到編解碼器特定擴充套件控制元件的影響,如每種格式的文件中所述。

兩個佇列獨立執行,遵循 V4L2 緩衝區佇列和記憶體到記憶體裝置的標準行為。此外,由於所選編碼格式的特性,例如幀重新排序,從 CAPTURE 隊列出隊的編碼幀順序可能與原始幀入隊到 OUTPUT 佇列的順序不同。

客戶端不得假設 CAPTUREOUTPUT 緩衝區之間有任何直接關係,也不得假設緩衝區可出隊的任何特定時序。具體來說:

  • 一個入隊到 OUTPUT 的緩衝區可能會導致在 CAPTURE 上產生多個緩衝區(例如,如果返回一個編碼幀允許編碼器返回一個在顯示上領先但在解碼順序上滯後的幀;然而,也可能有其他原因),

  • 一個入隊到 OUTPUT 的緩衝區可能會導致在編碼過程後期和/或處理更多 OUTPUT 緩衝區後才在 CAPTURE 上產生一個緩衝區,或者無序返回,例如在使用顯示重新排序時;

  • 緩衝區可能會在 CAPTURE 佇列上可用,而無需額外緩衝區入隊到 OUTPUT(例如在排空或 EOS 期間),這是因為過去入隊的 OUTPUT 緩衝區,其編碼結果由於編碼過程的特殊性,僅在稍後可用;

  • 入隊到 OUTPUT 的緩衝區可能不會在編碼到相應的 CAPTURE 緩衝區後立即變得可出隊,例如,如果編碼器需要將該幀用作編碼後續幀的參考。

注意

為了允許將編碼後的 CAPTURE 緩衝區與其源自的 OUTPUT 緩衝區進行匹配,客戶端可以在 OUTPUT 緩衝區入隊時設定 timestamp 欄位的 v4l2_buffer 結構體。從該 OUTPUT 緩衝區編碼而來的 CAPTURE 緩衝區,在出隊時其 timestamp 欄位將被設定為相同的值。

除了一個 OUTPUT 緩衝區產生一個 CAPTURE 緩衝區的直接情況外,還定義了以下情況:

  • 一個 OUTPUT 緩衝區生成多個 CAPTURE 緩衝區:相同的 OUTPUT 時間戳將複製到多個 CAPTURE 緩衝區;

  • 編碼順序與呈現順序不同(即 CAPTURE 緩衝區相對於 OUTPUT 緩衝區是無序的):CAPTURE 時間戳將不保留 OUTPUT 時間戳的順序。

注意

為了讓客戶端區分幀型別(關鍵幀、中間幀;確切的型別列表取決於編碼格式),在 CAPTURE 緩衝區出隊時,其 v4l2_buffer 結構體中將設定相應的標誌位。有關標誌的精確列表及其含義,請參閱 v4l2_buffer 和每種編碼畫素格式的文件。

如果發生編碼錯誤,將根據編碼器能力以詳細程度報告給客戶端。具體來說:

  • 包含失敗編碼操作結果的 CAPTURE 緩衝區(如果有)將返回並設定 V4L2_BUF_FLAG_ERROR 標誌;

  • 如果編碼器能夠精確報告觸發錯誤的 OUTPUT 緩衝區,則這些緩衝區將返回並設定 V4L2_BUF_FLAG_ERROR 標誌。

注意

如果 CAPTURE 緩衝區太小,則它會直接返回並設定 V4L2_BUF_FLAG_ERROR 標誌。需要更多工作來檢測此錯誤是否因緩衝區過小而發生,並提供支援以釋放過小的現有緩衝區。

如果發生不允許編碼繼續的致命故障,對相應編碼器檔案控制代碼的任何進一步操作都將返回 -EIO 錯誤碼。客戶端可以關閉檔案控制代碼並開啟一個新的,或者透過停止兩個佇列的流式傳輸、釋放所有緩衝區並再次執行初始化序列來重新初始化例項。

4.5.2.7. 編碼引數更改

客戶端允許隨時使用 VIDIOC_S_CTRL() 更改編碼器引數。引數的可用性取決於編碼器,客戶端必須查詢編碼器以找到可用的控制元件集合。

在編碼期間更改每個引數的能力取決於編碼器,並遵循 V4L2 控制介面的標準語義。客戶端可以在編碼期間嘗試設定控制元件,如果操作失敗並返回 -EBUSY 錯誤碼,則需要停止 CAPTURE 佇列才能允許配置更改。為此,它可以遵循 排空 序列,以避免丟失已入隊/編碼的幀。

引數更新的時序取決於編碼器,並遵循 V4L2 控制介面的標準語義。如果客戶端需要在特定幀上精確應用引數,則應考慮使用請求 API(請求 API),如果編碼器支援的話。

4.5.2.8. 排空

為了確保所有已入隊的 OUTPUT 緩衝區已處理完畢,並且相關的 CAPTURE 緩衝區已交給客戶端,客戶端必須遵循以下所述的排空序列。排空序列結束後,客戶端已接收所有在序列開始前入隊的 OUTPUT 緩衝區的所有編碼幀。

  1. 透過發出 VIDIOC_ENCODER_CMD() 開始排空序列。

    • 必需欄位

      cmd

      設定為 V4L2_ENC_CMD_STOP

      flags

      設定為 0。

      pts

      設定為 0。

    警告

    只有當 OUTPUTCAPTURE 佇列都在流式傳輸時,才能啟動該序列。出於相容性原因,即使任一佇列未在流式傳輸,對 VIDIOC_ENCODER_CMD() 的呼叫也不會失敗,但同時它也不會啟動 排空 序列,因此下面描述的步驟將不適用。

  2. 客戶端在發出 VIDIOC_ENCODER_CMD() 之前入隊的任何 OUTPUT 緩衝區都將正常處理和編碼。客戶端必須繼續獨立處理兩個佇列,類似於正常編碼操作。這包括:

    • CAPTURE 緩衝區進行入隊和出隊操作,直到出隊一個標記有 V4L2_BUF_FLAG_LAST 標誌的緩衝區;

      警告

      最後一個緩衝區可能為空(v4l2_buffer bytesused = 0),在這種情況下,客戶端必須忽略它,因為它不包含編碼幀。

      注意

      任何嘗試出隊超出標記有 V4L2_BUF_FLAG_LAST 標誌的緩衝區之外的 CAPTURE 緩衝區都將導致 VIDIOC_DQBUF() 返回 -EPIPE 錯誤。

    • 對已處理的 OUTPUT 緩衝區進行出隊操作,直到在 V4L2_ENC_CMD_STOP 命令之前入隊的所有緩衝區都已出隊;

    • 出隊 V4L2_EVENT_EOS 事件,如果客戶端訂閱了它。

    注意

    為了向後相容性,當最後一幀已編碼且所有幀都準備好出隊時,編碼器將發出 V4L2_EVENT_EOS 事件。這是已棄用的行為,客戶端不得依賴它。應該改用 V4L2_BUF_FLAG_LAST 緩衝區標誌。

  3. 一旦在 V4L2_ENC_CMD_STOP 呼叫之前入隊的所有 OUTPUT 緩衝區都已出隊,並且最後一個 CAPTURE 緩衝區已出隊,編碼器將停止,它將接受但不處理任何新入隊的 OUTPUT 緩衝區,直到客戶端發出以下任何操作:

    • V4L2_ENC_CMD_START - 編碼器將不會復位,並將正常恢復操作,保留排空前的所有狀態;

    • CAPTURE 佇列上進行一對 VIDIOC_STREAMOFF()VIDIOC_STREAMON() 操作 - 編碼器將復位(參見 復位 序列)然後恢復編碼;

    • OUTPUT 佇列上進行一對 VIDIOC_STREAMOFF()VIDIOC_STREAMON() 操作 - 編碼器將正常恢復操作,但任何在 V4L2_ENC_CMD_STOPVIDIOC_STREAMOFF() 之間入隊到 OUTPUT 佇列的源幀將被丟棄。

注意

一旦排空序列啟動,客戶端需要驅動它完成,如上述步驟所述,除非它透過在 OUTPUTCAPTURE 佇列上發出 VIDIOC_STREAMOFF() 來中止該過程。在排空序列進行中時,客戶端不允許再次發出 V4L2_ENC_CMD_STARTV4L2_ENC_CMD_STOP,如果嘗試,它們將以 -EBUSY 錯誤碼失敗。

作為參考,下面描述了各種邊界情況的處理:

  • 如果在發出 V4L2_ENC_CMD_STOP 命令時 OUTPUT 佇列中沒有緩衝區,則排空序列立即完成,編碼器返回一個設定了 V4L2_BUF_FLAG_LAST 標誌的空 CAPTURE 緩衝區。

  • 如果在排空序列完成時 CAPTURE 佇列中沒有緩衝區,則客戶端下次入隊 CAPTURE 緩衝區時,它會立即作為設定了 V4L2_BUF_FLAG_LAST 標誌的空緩衝區返回。

  • 如果在排空序列進行中呼叫 VIDIOC_STREAMOFF()CAPTURE 佇列上,則排空序列被取消,所有 CAPTURE 緩衝區都隱式返回給客戶端。

  • 如果在排空序列進行中呼叫 VIDIOC_STREAMOFF()OUTPUT 佇列上,則排空序列立即完成,下一個 CAPTURE 緩衝區將作為空緩衝區返回並設定 V4L2_BUF_FLAG_LAST 標誌。

儘管不是強制性的,編碼器命令的可用性可以使用 VIDIOC_TRY_ENCODER_CMD() 進行查詢。

4.5.2.9. 復位

客戶端可能希望請求編碼器重新初始化編碼,以便後續流資料獨立於之前生成的流資料。根據編碼格式,這可能意味著:

  • 重新啟動後生成的編碼幀不得引用停止前生成的任何幀,例如 H.264/HEVC 不得有長期參考幀;

  • 任何必須包含在獨立流中的標頭檔案必須再次生成,例如 H.264/HEVC 的 SPS 和 PPS。

這可以透過執行復位序列來實現。

  1. 執行 排空 序列,以確保所有進行中的編碼完成且相應緩衝區出隊。

  2. 透過 VIDIOC_STREAMOFF() 停止 CAPTURE 佇列上的流式傳輸。這將把所有當前入隊的 CAPTURE 緩衝區返回給客戶端,不包含有效幀資料。

  3. 透過 VIDIOC_STREAMON()CAPTURE 佇列上開始流式傳輸,並繼續常規編碼序列。從此以後,編碼到 CAPTURE 緩衝區中的編碼幀將包含一個獨立流,可以解碼,無需復位序列之前編碼的幀,從發出 排空 序列的 V4L2_ENC_CMD_STOP 命令後入隊的第一個 OUTPUT 緩衝區開始。

此序列也可以用於沒有即時更改引數能力的編碼器來更改編碼引數。

4.5.2.10. 提交點

設定格式和分配緩衝區會觸發編碼器行為的變化。

  1. CAPTURE 佇列上設定格式可能會更改 OUTPUT 佇列上支援/通告的格式集合。特別是,這也意味著 OUTPUT 格式可能會被複位,客戶端不得依賴於之前設定的格式得到保留。

  2. 列舉 OUTPUT 佇列上的格式總是隻返回當前 CAPTURE 格式支援的格式。

  3. OUTPUT 佇列上設定格式不會改變 CAPTURE 佇列上可用格式的列表。嘗試設定當前所選 CAPTURE 格式不支援的 OUTPUT 格式將導致編碼器將請求的 OUTPUT 格式調整為受支援的格式。

  4. 列舉 CAPTURE 佇列上的格式總是返回支援的完整編碼格式集合,不論當前的 OUTPUT 格式是什麼。

  5. 當緩衝區已在 OUTPUTCAPTURE 佇列上分配時,客戶端不得更改 CAPTURE 佇列上的格式。對於任何此類格式更改嘗試,驅動將返回 -EBUSY 錯誤碼。

總而言之,設定格式和分配必須始終從 CAPTURE 佇列開始,並且 CAPTURE 佇列是管理 OUTPUT 佇列支援格式集合的主體。