摘要:本期就分享幾個關於DVPP影片解碼問題的典型案例,並給出原因分析及解決方法
本文分享自華為雲社群《DVPP媒體資料處理影片解碼問題案例》,作者:昇騰CANN 。
DVPP(Digital Vision Pre-Processing)是昇騰AI處理器內建的影像處理單元,透過AscendCL媒體資料處理介面提供強大的媒體處理硬加速能力,主要功能包括影像編解碼、影片編解碼、影像摳圖縮放等。
本期就分享幾個關於DVPP影片解碼問題的典型案例,並給出原因分析及解決方法:
- 影片解碼程式卡死,無法退出
- retCode返回值設定錯誤,導致影片解碼異常
- 影片解碼無報錯,但無解碼結果資料,且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]昇騰論壇