4.5.3. 記憶體到記憶體無狀態影片解碼器介面

無狀態解碼器是指在處理幀之間不保留任何狀態的解碼器。這意味著每幀都是獨立於任何之前和未來的幀進行解碼的,並且客戶端負責維護解碼狀態並將其提供給解碼器,每次解碼請求都如此。這與有狀態影片解碼器介面形成對比,在有狀態解碼器介面中,硬體和驅動程式維護解碼狀態,客戶端只需提供原始編碼流並按顯示順序出隊解碼後的幀。

本節描述了使用者空間(“客戶端”)如何與無狀態解碼器進行通訊,以成功解碼編碼流。與有狀態編解碼器相比,解碼器/客戶端序列更簡單,但這種簡單性帶來的代價是客戶端的額外複雜性,客戶端負責維護一致的解碼狀態。

無狀態解碼器使用請求 API。當呼叫VIDIOC_REQBUFS()VIDIOC_CREATE_BUFS()時,無狀態解碼器必須在其OUTPUT佇列上公開V4L2_BUF_CAP_SUPPORTS_REQUESTS能力。

根據解碼器支援的編碼格式,單個解碼幀可能由多個解碼請求(例如,每幀包含多個切片的 H.264 流)的結果。支援此類格式的解碼器還必須在其OUTPUT佇列上公開V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF能力。

4.5.3.1. 查詢能力

  1. 要列舉解碼器支援的編碼格式集,客戶端在OUTPUT佇列上呼叫VIDIOC_ENUM_FMT()

    • 驅動程式必須始終返回完整的受支援OUTPUT格式集,無論CAPTURE佇列上當前設定的格式如何。

    • 同時,驅動程式必須將編解碼器特定能力控制(例如 H.264 配置檔案)返回的值集限制為硬體實際支援的集。

  2. 要列舉支援的原始格式集,客戶端在CAPTURE佇列上呼叫VIDIOC_ENUM_FMT()

    • 驅動程式必須只返回OUTPUT佇列上當前活動格式所支援的格式。

    • 根據當前設定的OUTPUT格式,支援的原始格式集可能取決於某些編解碼器相關控制元件的值。客戶端負責確保在查詢CAPTURE佇列之前設定這些控制元件。否則將使用這些控制元件的預設值,並且返回的格式集可能無法用於客戶端嘗試解碼的媒體。

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

  4. 如果適用,可以透過VIDIOC_QUERYCTRL()使用各自的控制元件查詢當前OUTPUT格式的支援的配置檔案和級別。

4.5.3.2. 初始化

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

    • 所需欄位

      型別

      適用於OUTPUTV4L2_BUF_TYPE_*列舉。

      畫素格式

      編碼畫素格式。

      width, height

      從流中解析出的編碼寬度和高度。

      其他欄位

      遵循標準語義。

    注意

    更改OUTPUT格式可能會更改當前設定的CAPTURE格式。驅動程式將從正在設定的OUTPUT格式派生一個新的CAPTURE格式,包括解析度、色度引數等。如果客戶端需要特定的CAPTURE格式,它必須在之後進行調整。

  2. 呼叫VIDIOC_S_EXT_CTRLS()設定OUTPUT格式所需的所有控制元件(解析的標頭檔案等),以列舉CAPTURE格式。

  3. CAPTURE佇列呼叫VIDIOC_G_FMT()以獲取從位元組流解析/解碼的目標緩衝區的格式。

    • 所需欄位

      型別

      適用於CAPTUREV4L2_BUF_TYPE_*列舉。

    • 返回欄位

      width, height

      解碼幀的幀緩衝區解析度。

      畫素格式

      解碼幀的畫素格式。

      num_planes (僅適用於 _MPLANE type)

      畫素格式的平面數。

      sizeimage, bytesperline

      按照標準語義;匹配幀緩衝區格式。

    注意

    pixelformat的值可以是OUTPUT格式支援的任何畫素格式,基於硬體能力。建議驅動程式為當前配置選擇首選/最優格式。例如,如果 RGB 需要額外的轉換步驟,則 YUV 格式可能優於 RGB 格式。

  4. [可選] 透過VIDIOC_ENUM_FMT()CAPTURE佇列上列舉CAPTURE格式。客戶端可以使用此 ioctl 發現當前OUTPUT格式支援哪些替代原始格式,並透過VIDIOC_S_FMT()選擇其中之一。

    注意

    驅動程式將只返回當前選定的OUTPUT格式和當前設定的控制元件所支援的格式,即使解碼器通常可能支援更多格式。

    例如,解碼器可能支援解析度為 1920x1088 及更低解析度的 YUV 和 RGB 格式,但對於更高解析度僅支援 YUV(由於硬體限制)。將 1920x1088 或更低解析度設定為OUTPUT格式後,VIDIOC_ENUM_FMT()可能會返回一組 YUV 和 RGB 畫素格式,但將解析度設定為高於 1920x1088 後,驅動程式將不再返回 RGB 畫素格式,因為此解析度不支援它們。

  5. [可選]CAPTURE佇列上透過VIDIOC_S_FMT()選擇與建議的CAPTURE格式不同的格式。客戶端可以選擇與驅動程式在VIDIOC_G_FMT()中選擇/建議的格式不同的格式。

    • 所需欄位

      型別

      適用於CAPTUREV4L2_BUF_TYPE_*列舉。

      畫素格式

      原始畫素格式。

      width, height

      解碼流的幀緩衝區解析度;通常與VIDIOC_G_FMT()返回的相同,但如果硬體支援合成和/或縮放,則可能不同。

    執行此步驟後,客戶端必須再次執行步驟 3,以獲取有關緩衝區大小和佈局的最新資訊。

  6. 透過VIDIOC_REQBUFS()OUTPUT佇列上分配源(位元組流)緩衝區。

    • 所需欄位

      計數

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

      型別

      適用於OUTPUTV4L2_BUF_TYPE_*列舉。

      記憶體

      遵循標準語義。

    • 返回欄位

      計數

      實際分配的緩衝區數量。

    • 如果需要,驅動程式將調整count,使其等於或大於給定格式所需OUTPUT緩衝區的最小數量和請求的計數。客戶端在 ioctl 返回後必須檢查此值以獲取實際分配的緩衝區數量。

  7. 透過VIDIOC_REQBUFS()CAPTURE佇列上分配目標(原始格式)緩衝區。

    • 所需欄位

      計數

      請求分配的緩衝區數量;大於零。客戶端負責推斷流正確解碼所需的最小緩衝區數量(例如,考慮參考幀)並傳入等於或更大的數量。

      型別

      適用於CAPTUREV4L2_BUF_TYPE_*列舉。

      記憶體

      遵循標準語義。V4L2_MEMORY_USERPTR不支援CAPTURE緩衝區。

    • 返回欄位

      計數

      調整為已分配的緩衝區數量,以防編解碼器需要比請求更多的緩衝區。

    • 驅動程式必須調整 count 以適應當前格式、流配置和請求計數所需的CAPTURE緩衝區最小數量。客戶端在 ioctl 返回後必須檢查此值以獲取已分配的緩衝區數量。

  8. 透過在媒體裝置上呼叫MEDIA_IOC_REQUEST_ALLOC()分配請求(可能每個OUTPUT緩衝區一個)。

    透過在媒體裝置上呼叫MEDIA_IOC_REQUEST_ALLOC()分配請求(可能每個OUTPUT緩衝區一個)。

  9. 透過VIDIOC_STREAMON()OUTPUTCAPTURE佇列上啟動流式傳輸

    VIDIOC_STREAMON().

4.5.3.3. 解碼

對於每個幀,客戶端負責提交至少一個請求,其中附加以下內容:

  • 編解碼器當前配置所需的編碼資料量,作為提交到OUTPUT佇列的緩衝區。通常,這對應於一幀的編碼資料,但某些格式可能允許(或要求)每單位不同的量。

  • 解碼提交的編碼資料所需的所有元資料,以與正在解碼的格式相關的控制元件形式。

OUTPUT緩衝區的資料量和內容,以及必須在請求上設定的控制元件,取決於活動編碼畫素格式,並可能受編解碼器特定擴充套件控制元件的影響,如每種格式的文件所述。

如果解碼後的幀可能需要在當前請求之後一個或多個解碼請求才能生成,則客戶端必須在OUTPUT緩衝區上設定V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF標誌。這將導致(可能部分)解碼的CAPTURE緩衝區不被提供用於出隊,如果下一個OUTPUT緩衝區的時間戳沒有改變,則將其重複用於下一個解碼請求。

一個典型的幀將使用以下序列進行解碼

  1. 使用VIDIOC_QBUF()將一個包含一單位編碼位元組流資料的OUTPUT緩衝區排隊,用於解碼請求。

    • 所需欄位

      索引

      正在排隊的緩衝區的索引。

      型別

      緩衝區的型別。

      已用位元組數

      緩衝區中編碼資料幀佔用的位元組數。

      標誌

      必須設定V4L2_BUF_FLAG_REQUEST_FD標誌。此外,如果我們不確定當前解碼請求是生成完全解碼幀所需的最後一個請求,那麼V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF也必須設定。

      請求_fd

      必須設定為解碼請求的檔案描述符。

      時間戳

      每幀必須設定為唯一值。此值將傳播到解碼幀的緩衝區中,也可用於將此幀用作另一個幀的參考。如果每幀使用多個解碼請求,則給定幀的所有OUTPUT緩衝區的時間戳必須相同。如果時間戳改變,則當前持有的CAPTURE緩衝區將可用於出隊,當前請求將處理一個新的CAPTURE緩衝區。

  2. 使用VIDIOC_S_EXT_CTRLS()設定解碼請求的編解碼器特定控制元件。

    • 所需欄位

      哪個

      必須是V4L2_CTRL_WHICH_REQUEST_VAL

      請求_fd

      必須設定為解碼請求的檔案描述符。

      其他欄位

      設定控制元件時,其他欄位照常設定。controls陣列必須包含解碼幀所需的所有編解碼器特定控制元件。

    注意

    可以在不同次呼叫VIDIOC_S_EXT_CTRLS()中指定控制元件,或者覆蓋先前設定的控制元件,只要request_fdwhich設定正確。提交請求時的控制元件狀態將被考慮。

    注意

    步驟 1 和 2 的發生順序可以互換。

  3. 透過在請求 FD 上呼叫MEDIA_REQUEST_IOC_QUEUE()提交請求。

    如果請求在沒有OUTPUT緩衝區的情況下提交,或者請求中缺少某些必需的控制元件,則MEDIA_REQUEST_IOC_QUEUE()將返回-ENOENT。如果排隊了多個OUTPUT緩衝區,則將返回-EINVALMEDIA_REQUEST_IOC_QUEUE()返回非零表示此請求不會產生CAPTURE緩衝區。

CAPTURE緩衝區不得作為請求的一部分,並且獨立排隊。它們按解碼順序返回(即,與編碼幀提交到OUTPUT佇列的順序相同)。

執行時解碼錯誤透過攜帶V4L2_BUF_FLAG_ERROR標誌的出隊CAPTURE緩衝區發出訊號。如果解碼的參考幀有錯誤,則所有引用它的後續解碼幀也設定V4L2_BUF_FLAG_ERROR標誌,儘管解碼器仍將嘗試生成(可能已損壞的)幀。

4.5.3.4. 解碼時的緩衝區管理

與有狀態解碼器相反,無狀態解碼器不執行任何形式的緩衝區管理:它只保證出隊的CAPTURE緩衝區只要未再次入隊,就可以供客戶端使用。“使用”在這裡包括將緩衝區用於合成或顯示。

出隊的捕獲緩衝區也可以用作另一個緩衝區的參考幀。

透過將其時間戳轉換為納秒,並將其儲存在編解碼器相關控制結構的相關成員中,可以將幀指定為參考。必須使用v4l2_timeval_to_ns()函式執行該轉換。只要其所有編碼資料單元都成功提交到OUTPUT佇列,就可以使用幀的時間戳將其引用。

包含參考幀的解碼緩衝區在所有引用它的幀被解碼之前,不得再次用作解碼目標。實現這一點的最安全方法是,在所有引用它的解碼幀都被出隊之前,避免將參考緩衝區排隊。但是,如果驅動程式可以保證排隊到CAPTURE佇列的緩衝區按排隊順序處理,那麼使用者空間可以利用此保證並在滿足以下條件時將參考緩衝區排隊:

  1. 所有受參考幀影響的幀的請求已排隊,並且

  2. 已排隊足夠數量的CAPTURE緩衝區以覆蓋所有解碼的引用幀。

當排隊解碼請求時,驅動程式將增加與參考幀關聯的所有資源的引用計數。這意味著客戶端可以(例如)在之後不需要時關閉參考幀緩衝區的 DMABUF 檔案描述符。

4.5.3.5. 搜尋

為了進行搜尋,客戶端只需使用與新流位置對應的輸入緩衝區提交請求。但是,它必須意識到解析度可能已更改,並在這種情況下遵循動態解析度更改序列。此外,根據所使用的編解碼器,圖片引數(例如 H.264 的 SPS/PPS)可能已更改,客戶端負責確保將有效狀態傳送到解碼器。

然後,客戶端可以自由忽略來自搜尋前位置的任何返回的CAPTURE緩衝區。

4.5.3.6. 暫停

為了暫停,客戶端只需停止將緩衝區排隊到OUTPUT佇列。沒有源位元組流資料,就沒有要處理的資料,編解碼器將保持空閒。

4.5.3.7. 動態解析度更改

如果客戶端檢測到流中的解析度變化,它將需要使用新解析度再次執行初始化序列

  1. 如果最後提交的請求由於使用V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF標誌而導致CAPTURE緩衝區被持有,則最後一幀在CAPTURE佇列中不可用。在這種情況下,應傳送V4L2_DEC_CMD_FLUSH命令。這將使驅動程式出隊被持有的CAPTURE緩衝區。

  2. 等待所有提交的請求完成並出隊相應的輸出緩衝區。

  3. OUTPUTCAPTURE佇列上呼叫VIDIOC_STREAMOFF()

  4. 透過在CAPTURE佇列上呼叫VIDIOC_REQBUFS()並將緩衝區計數設定為零來釋放所有CAPTURE緩衝區。

  5. OUTPUT佇列上設定新解析度後,再次執行初始化序列(減去OUTPUT緩衝區的分配)。請注意,由於解析度限制,可能需要在CAPTURE佇列上選擇不同的格式。

4.5.3.8. 排空

如果最後提交的請求由於使用V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF標誌而導致CAPTURE緩衝區被持有,則最後一幀在CAPTURE佇列中不可用。在這種情況下,應傳送V4L2_DEC_CMD_FLUSH命令。這將使驅動程式出隊被持有的CAPTURE緩衝區。

之後,為了排空無狀態解碼器上的流,客戶端只需等待所有提交的請求完成。