2016-07-29 62 views
0

我有一個與Nvidias NVenc API相關的問題。我想使用API​​來編碼一些OpenGL圖形。我的問題是,API在整個程序中報告沒有錯誤,一切似乎都很好。但是生成的輸出不可被例如VLC。如果我嘗試播放生成的文件,VLC將閃爍約0.5秒的黑屏,然後結束播放。 視頻的長度爲0,Vid的大小似乎也很小。 分辨率爲1280 * 720,而5secs錄音的大小隻有700kb。這是現實的嗎?NVencs輸出比特流不可讀

應用程序的流程如下:

  1. 渲染到次幀緩衝區
  2. 下載Framebuffer的兩個公益組織中的一個(glReadPixels())
  3. 地圖的前一幀的PBO,至得到Cuda可以理解的指針。
  4. 根據this(第18頁)調用一個簡單的CudaKernel,將OpenGLs RGBA轉換爲ARGB,NVenc應該可以理解這一點。內核讀取PBO的內容並將轉換後的內容寫入CudaArray(使用cudaMalloc創建),該CudaArray使用NVenc註冊爲InputResource。
  5. 轉換後的數組的內容被編碼。一個完成事件加上相應的輸出比特流緩衝區得到排隊。
  6. 輔助線程監聽排隊的輸出事件,如果發送了一個事件,輸出位流被映射並寫入硬盤。

NVenc-編碼器的initializion:

InitParams* ip = new InitParams(); 
m_initParams = ip; 
memset(ip, 0, sizeof(InitParams)); 
ip->version = NV_ENC_INITIALIZE_PARAMS_VER; 
ip->encodeGUID = m_encoderGuid; //Used Codec 
ip->encodeWidth = width; // Frame Width 
ip->encodeHeight = height; // Frame Height 
ip->maxEncodeWidth = 0; // Zero means no dynamic res changes 
ip->maxEncodeHeight = 0; 
ip->darWidth = width; // Aspect Ratio 
ip->darHeight = height; 
ip->frameRateNum = 60; // 60 fps 
ip->frameRateDen = 1; 
ip->reportSliceOffsets = 0; // According to programming guide 
ip->enableSubFrameWrite = 0; 
ip->presetGUID = m_presetGuid; // Used Preset for Encoder Config 

NV_ENC_PRESET_CONFIG presetCfg; // Load the Preset Config 
memset(&presetCfg, 0, sizeof(NV_ENC_PRESET_CONFIG)); 
presetCfg.version = NV_ENC_PRESET_CONFIG_VER; 
presetCfg.presetCfg.version = NV_ENC_CONFIG_VER; 
CheckApiError(m_apiFunctions.nvEncGetEncodePresetConfig(m_Encoder, 
    m_encoderGuid, m_presetGuid, &presetCfg)); 
memcpy(&m_encodingConfig, &presetCfg.presetCfg, sizeof(NV_ENC_CONFIG)); 
// And add information about Bitrate etc 
m_encodingConfig.rcParams.averageBitRate = 500000; 
m_encodingConfig.rcParams.maxBitRate = 600000; 
m_encodingConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_MODE::NV_ENC_PARAMS_RC_CBR; 
ip->encodeConfig = &m_encodingConfig; 
ip->enableEncodeAsync = 1; // Async Encoding 
ip->enablePTD = 1; // Encoder handles picture ordering 

CudaResource登記

m_cuContext->SetCurrent(); // Make the clients cuCtx current 
NV_ENC_REGISTER_RESOURCE res; 
memset(&res, 0, sizeof(NV_ENC_REGISTER_RESOURCE)); 
NV_ENC_REGISTERED_PTR resPtr; // handle to the cuda resource for future use 
res.bufferFormat = m_inputFormat; // Format is ARGB 
res.height = m_height; 
res.width = m_width; 
// NOTE: I've set the pitch to the width of the frame, because the resource is a non-pitched 
//cudaArray. Is this correct? Pitch = 0 would produce no output. 
res.pitch = pitch; 
res.resourceToRegister = (void*) (uintptr_t) resourceToRegister; //CUdevptr to resource 
res.resourceType = 
    NV_ENC_INPUT_RESOURCE_TYPE::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; 
res.version = NV_ENC_REGISTER_RESOURCE_VER; 
CheckApiError(m_apiFunctions.nvEncRegisterResource(m_Encoder, &res)); 
m_registeredInputResources.push_back(res.registeredResource); 

編碼

m_cuContext->SetCurrent(); // Make Clients context current 
MapInputResource(id); //Map the CudaInputResource 
NV_ENC_PIC_PARAMS temp; 
memset(&temp, 0, sizeof(NV_ENC_PIC_PARAMS)); 
temp.version = NV_ENC_PIC_PARAMS_VER; 
unsigned int currentBufferAndEvent = m_counter % m_registeredEvents.size(); //Counter is inc'ed in every Frame 
temp.bufferFmt = m_currentlyMappedInputBuffer.mappedBufferFmt; 
temp.inputBuffer = m_currentlyMappedInputBuffer.mappedResource; //got set by MapInputResource 
temp.completionEvent = m_registeredEvents[currentBufferAndEvent]; 
temp.outputBitstream = m_registeredOutputBuffers[currentBufferAndEvent]; 
temp.inputWidth = m_width; 
temp.inputHeight = m_height; 
temp.inputPitch = m_width; 
temp.inputTimeStamp = m_counter; 
temp.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; // According to samples 
temp.qpDeltaMap = NULL; 
temp.qpDeltaMapSize = 0; 

EventWithId latestEvent(currentBufferAndEvent, 
    m_registeredEvents[currentBufferAndEvent]); 
PushBackEncodeEvent(latestEvent); // Store the Event with its ID in a Queue 

CheckApiError(m_apiFunctions.nvEncEncodePicture(m_Encoder, &temp)); 
m_counter++; 
UnmapInputResource(id); // Unmap 

每一個小小的提示,在哪裏看,都非常感謝。我正在想出什麼可能是錯誤的。

非常感謝!

+0

你沒有給出太多的細節,但聽起來像處理RAW碼流時VLC的常見問題:如果沒有告訴編解碼器,VLC無法播放它們。爲此,請嘗試爲文件提供正確的結尾,例如用於h264編解碼器的「filename.h264」。 – kunzmi

+0

好的,如果我這樣做,我會得到以下結果:[點擊](https://s31.postimg.org/vvgbiqg0b/Encoded_File.png)。 – Christoph

+0

似乎有些問題已經在[交叉發佈]中進行了整理(https://devtalk.nvidia.com/default/topic/953041/gpu-accelerated-libraries/nvencs-output-bitstream-is-not-readable /)。 –

回答

1

在nvidia論壇的hall822的幫助下,我設法解決了這個問題。

主要的錯誤是我註冊了我的cuda資源,其音調等於幀的大小。我正在使用Framebuffer-Renderbuffer來繪製我的內容。這個數據是一個普通的,未修改的數組。我的第一個想法是,讓一個等於零的音高失敗。編碼器什麼也沒做。接下來的想法是將其設置爲幀的寬度,圖像的四分之一被編碼。

// NOTE: I've set the pitch to the width of the frame, because the resource is a non-pitched 
//cudaArray. Is this correct? Pitch = 0 would produce no output. 
res.pitch = pitch; 

回答這個問題:是的,它是正確的。但是音調是以字節來衡量的。因爲我編碼RGBA幀,所以正確的音調必須是FRAME_WIDTH * 4

第二個錯誤是我的顏色通道不正確(請參閱我的開場白中的第4點)。NVidia枚舉說編碼器期望ARGB格式的通道,但實際上是BGRA,所以總是255的阿爾法通道污染了藍色通道。

編輯:這可能是由於NVidia在內部使用小端。我正在將我的像素數據寫入 到一個字節數組,選擇像int32這樣的其他類型可能允許傳遞實際的ARGB數據。

+0

這是非常有趣的,因爲它可能是我有一個問題的真正答案,我發佈並回答(或認爲我回答)在這上:http://stackoverflow.com/questions/32924056/nvencregisterresource-fails-與-23。在我的代碼中,我在註冊CUDA資源時設置了參數,但我從未嘗試調用'nvEncEncodePicture()'來設置'inputPitch'。在我的情況下,該設置因此保持爲零,這對我而言是有效的,但只適用於<= 2560(這對我來說是可以接受的,因爲我使用的是NV12格式)。 – JPNotADragon