昇騰實戰丨DVPP媒體資料處理影片解碼問題案例

華為雲開發者聯盟發表於2023-05-08
摘要:本期就分享幾個關於DVPP影片解碼問題的典型案例,並給出原因分析及解決方法

本文分享自華為雲社群《DVPP媒體資料處理影片解碼問題案例》,作者:昇騰CANN 。

DVPP(Digital Vision Pre-Processing)是昇騰AI處理器內建的影像處理單元,透過AscendCL媒體資料處理介面提供強大的媒體處理硬加速能力,主要功能包括影像編解碼、影片編解碼、影像摳圖縮放等。

本期就分享幾個關於DVPP影片解碼問題的典型案例,並給出原因分析及解決方法:

  1. 影片解碼程式卡死,無法退出
  2. retCode返回值設定錯誤,導致影片解碼異常
  3. 影片解碼無報錯,但無解碼結果資料,且CPU佔用率高

01 影片解碼程式卡死,無法退出

現象描述

使用者程式卡死,無法退出。檢視應用類日誌,一直重複提示資訊“fault kernel_name=DvppSendVdecFrame”、“Kernel task happen error, retCode=0x28, [aicpu timeout]”,表示AI CPU異常,無法處理影片解碼任務,導致任務超時。

日誌片段舉例如下:

[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:878]1827 PreCheckTaskErr:[DVPP][DEFAULT]Kernel task happen error, retCode=0x28, [aicpu timeout].
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:676]1827 PrintAicpuErrorInfo:[DVPP][DEFAULT]Aicpu kernel execute failed, device_id=0, stream_id=177, task_id=4, fault so_name=libdvpp_kernels.so, fault kernel_name=DvppSendVdecFrame, fault op_name=, extend_info=.
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:878]1831 PreCheckTaskErr:[DVPP][DEFAULT]Kernel task happen error, retCode=0x28, [aicpu timeout].
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:676]1831 PrintAicpuErrorInfo:[DVPP][DEFAULT]Aicpu kernel execute failed, device_id=0, stream_id=170, task_id=8, fault so_name=libdvpp_kernels.so, fault kernel_name=DvppSendVdecFrame, fault op_name=, extend_info=.
[ERROR] RUNTIME(pid,pName):DateTimeMS [engine.cc:960]1766 ReportExceptProc:[DVPP][DEFAULT]Task exception! device_id=0, stream_id=107, task_id=8, type=1, retCode=0x28.
[ERROR] RUNTIME(pid,pName):DateTimeMS [engine.cc:960]1773 ReportExceptProc:[DVPP][DEFAULT]Task exception! device_id=0, stream_id=130, task_id=4, type=1, retCode=0x28.

可能原因

Device記憶體不足,AI CPU無法處理影片解碼任務,導致任務超時。

處理步驟

1.在使用媒體資料處理V1版本的影片解碼功能前,可參考效能指標說明頁面中的“每路VDEC解碼的記憶體消耗計算公式”,預估需使用的Device記憶體,併合理規劃Device上的記憶體。
您可以在頁面左上側切換版本,檢視對應版本的效能指標說明。

2.最佳化應用程式的程式碼邏輯,增加異常處理機制,獲取影片解碼異常資訊,強制退出程式。

在呼叫aclinit介面初始化之後、呼叫aclvdecSendFrame介面解碼之前,定義異常回撥函式,並呼叫aclrtSetExceptionInfoCallback介面設定異常回撥函式,用於獲取任務異常資訊,以便在異常分支中根據任務異常資訊來判斷是否退出應用程式。

  • 定義異常回撥函式,回撥函式原型為:typedef void (*aclrtExceptionInfoCallback)(aclrtExceptionInfo *exceptionInfo)
  • 實現異常回撥函式,在異常回撥函式fn內呼叫aclrtGetDeviceIdFromExceptionInfo、aclrtGetStreamIdFromExceptionInfo、aclrtGetTaskIdFromExceptionInfo介面分別獲取Device ID、Stream ID、Task ID。

根據Stream ID、Task ID判斷Device是否異常,若異常,則強制退出程式。

異常回撥函式實現示例如下:

void dvpp_callback(aclrtExceptionInfo * exception_info)
{
    uint32_t taskId = aclrtGetTaskIdFromExceptionInfo(exception_info);
    uint32_t streamId = aclrtGetStreamIdFromExceptionInfo(exception_info);
    uint32_t deviceId = aclrtGetDeviceIdFromExceptionInfo(exception_info);
 if(taskId == 0xffffffff) || (streamId == 0xffffffff) {
 //Device異常,強制退出程式
 } else {
 //任務異常,如果頻繁出現(例如,統計1秒內觸發異常回撥函式的次數),程式退出
 }
 return;
}

3.呼叫aclrtSetExceptionInfoCallback介面設定異常回撥函式。

02 retCode返回值設定錯誤,導致影片解碼異常

現象描述

呼叫aclvdecSendFrame介面傳送一幀碼流後,繼續複用輸出圖片描述資訊,進行後續幀碼流的解碼操作,結果反覆出現解碼不成功、解碼異常的情況。

日誌片段舉例如下:

Channel[0]: success to aclvdecSendFrame, loop=1, count=7
get frame success, totalCount=7
packet.size is 26084.
Channel[0]: begin to send frame, loop=1, count=8
acldvppGetPicDescRetCode, retCode: 2.
Vdec ERROR!!!!!!!!!!!!!!!!
errCount is 3. total count is 3.
!!!!!!!!!!!!!!!!!!acldvppGetPicDescRetCode, retCode: 2.right_count:0,fail_count:3,total_count:3
Channel[0]: success to aclvdecSendFrame, loop=1, count=8
get frame success, totalCount=8
packet.size is 27927.
Channel[0]: begin to send frame, loop=1, count=9
acldvppGetPicDescRetCode, retCode: 2.
Vdec ERROR!!!!!!!!!!!!!!!!
errCount is 4. total count is 4.
!!!!!!!!!!!!!!!!!!acldvppGetPicDescRetCode, retCode: 2.right_count:0,fail_count:4,total_count:4

可能原因

根據日誌中的提示,透過acldvppGetPicDescRetCode介面獲取到的retCode為2,retCode為非0值時,表示解碼異常。

再檢視程式碼邏輯時,發現由於前一幀碼流解碼失敗,retCode被置為2,在複用輸出圖片描述資訊時,retCode也繼承了前一幀解碼失敗的狀態值2,導致AscendCL在解碼後續幀時,獲取到retCode值為2,就一直判斷解碼是失敗。

處理步驟

如果存在複用輸出圖片描述資訊的場景,需先呼叫acldvppSetPicDescRetCode設定為0,防止前一幀解碼異常的狀態影響後續解碼。

03 影片解碼無報錯,但無解碼結果資料、CPU佔用率高

現象描述

檢視應用類日誌,無ERROR報錯、無解碼結果資料輸出,另外,在執行應用程式的Linux伺服器上執行top命令,該應用程式的CPU佔用率持續升高。

可能原因

1. 無ERROR、無解碼結果資料輸出,初步判斷可能是因為解碼發幀介面aclvdecSendFrame呼叫正常,但未觸發回撥函式,無法獲取解碼結果資料。

2. 檢查觸發回撥函式的程式碼邏輯。

按照影片解碼的介面呼叫邏輯:由使用者提前建立一個單獨的執行緒,並自定義執行緒函式,線上程函式內呼叫aclrtProcessReport介面,透過該介面配置超時時間,等待指定的超時時間後,觸發回撥函式,獲取解碼結果資料。

Channel[0]: success to aclvdecSendFrame, loop=1, count=7
get frame success, totalCount=7
void *ThreadFunc(aclrtContext sharedContext)
{
 if (sharedContext == nullptr) {
 ERROR_LOG("sharedContext can not be nullptr");
 return ((void*)(-1));
 }
 INFO_LOG("use shared context for this thread");
 aclError ret = aclrtSetCurrentContext(sharedContext);
 if (ret != ACL_SUCCESS) {
 ERROR_LOG("aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(ret));
 return ((void*)(-1));
 }
 INFO_LOG("thread start ");
 while (runFlag) {
 // Notice: timeout 1000ms
 (void)aclrtProcessReport(1000);
 }
 return (void*)0;
}

3. 如果觸發回撥函式的介面呼叫邏輯正確,則在aclrtProcessReport介面處增加日誌列印,判斷應用執行過程中執行緒是否成功呼叫了aclrtProcessReport介面,只有成功呼叫aclrtProcessReport介面,才會觸發回撥函式。

示例程式碼如下:

while (runFlag) {
 // Notice: timeout 1000ms
 aclError ret = aclrtProcessReport(1000);
 printf("aclrtProcessReport failed, ret=%d.\n", ret);
}

4. 修改程式碼後,重新編譯執行應用。

在終端螢幕重複出現以下列印資訊,表示呼叫aclrtProcessReport介面失敗:

aclrtProcessReport failed, ret = 107012

查閱該介面的返回值說明,107012表示執行緒未訂閱或重複訂閱。

5. 檢查程式碼邏輯,檢查是否呼叫aclvdecSetChannelDescThreadId介面繫結使用者新建的執行緒,按照VDEC影片解碼的介面呼叫邏輯,只有呼叫該介面繫結使用者執行緒,才可以觸發呼叫aclrtProcessReport介面,進而觸發回撥函式。

6. 修改程式碼後,重新編譯執行應用,影片解碼正常,正常輸出解碼結果資料,同時CPU佔用率下降。

處理步驟

參見影片解碼的介面呼叫流程頁面或者參考VDEC功能樣例開發影片解碼功能。您可以在頁面左上側切換版本,檢視對應版本的介面呼叫流程。

其中,需關注以下注意點:

  • 建立新執行緒,並自定義執行緒函式,線上程函式內呼叫aclrtProcessReport介面,等待指定時間後,觸發回撥函式中的回撥函式。
  • 需呼叫aclvdecSetChannelDescThreadId介面繫結使用者建立的新執行緒。
  • 釋放資源時,依次銷燬通道、銷燬通道描述資訊後,才可以銷燬中使用者建立的新執行緒。

04 更多介紹

[1]昇騰檔案中心

[2]昇騰社群線上課程

[3]昇騰論壇

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章