2016-07-29 10 views
0

Nvidias NVenc API와 관련된 한 가지 질문이 있습니다. API를 사용하여 일부 OpenGL 그래픽을 인코딩하려고합니다. 내 문제는 API가 전체 프로그램에서 오류를보고하지 않는다는 것입니다. 모든 것이 정상적인 것 같습니다. 그러나 생성 된 출력은 예를 들어. VLC. 생성 된 파일을 재생하려고하면 VLC가 약 0.5 초 동안 검은 색 화면을 깜박이고 재생이 끝납니다. 비디오의 길이는 0이며 Vid의 크기는 약간 작습니다. 해상도는 1280 * 720이며 5 초 녹음의 크기는 700kb에 불과합니다. 이게 현실적인가요?NVencs 출력 비트 스트림을 읽을 수 없습니다.

응용 프로그램의 흐름은 다음과 같습니다 :

  1. 렌더링이 PBOs 중 하나에 보조 프레임 버퍼
  2. 다운로드 프레임 버퍼에 (glReadPixels())
  3. 에 이전 프레임의 PBO지도 Cuda가 이해할 수있는 포인터를 얻는다.
  4. this (p.18)에 따라 NVenc에서 이해할 수있는 OpenGLs RGBA를 ARGB로 변환하는 간단한 CudaKernel을 호출하십시오. 커널은 PBO의 내용을 읽고 변환 된 내용을 CudaArray (cudaMalloc을 사용하여 생성됨)에 기록합니다.이 CudaArray는 NVenc와 함께 InputResource로 등록됩니다.
  5. 변환 된 배열의 내용이 인코딩됩니다. 완료 이벤트와 해당 출력 비트 스트림 버퍼가 대기열에 추가됩니다.
  6. 보조 스레드가 대기중인 출력 이벤트를 수신하면 하나의 이벤트가 신호를 보내면 출력 비트 스트림이 매핑되고 hdd에 기록됩니다.

NVenc 인코더의 initializion : CudaResource

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 

등록

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는 코덱에 알려지지 않은 경우 VLC에서 재생할 수 없습니다. 이렇게하려면 파일에 오른쪽 끝자리 (예 : h264 코덱의 경우 "filename.h264"입니다. – kunzmi

+0

좋아, 그렇게하면 다음 결과를 얻습니다. [click] (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의 도움으로 문제를 해결할 수있었습니다.

기본 오류는 프레임의 크기와 동일한 피치로 나의 쿠다 리소스를 등록했다는 것입니다. 내 콘텐츠를 그리기 위해 Framebuffer-Renderbuffer를 사용하고 있습니다. 이 데이터는 일반 배열입니다. 제로와 같은 피치를주는 나의 첫번째 생각은 실패했습니다. 엔코더는 아무 것도하지 않았습니다. 다음 아이디어는 프레임 너비로 설정하고 이미지의 1/4을 인코딩했습니다.

// 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 enum은 엔코더가 ARGB 형식의 채널을 예상하지만 실제로는 BGRA이므로 항상 255 인 알파 채널이 파란색 채널을 오염 시켰습니다.

편집 : 이것은 NVidia가 내부적으로 리틀 엔디안을 사용하고 있기 때문일 수 있습니다. 내가 바이트 배열에 내 픽셀 데이터를 쓰고있어, int32와 같은 다른 유형을 선택하면 실제 ARGB 데이터를 전달할 수 있습니다.

+0

이것은 내가 가지고있는 문제에 대한 진정한 해답이 될 수 있기 때문에 매우 흥미 롭습니다. 제가 게시하여 답변을 드렸습니다. (또는 내가 대답 한 것 같아서) SO : http://stackoverflow.com/questions/32924056/nvencregisterresource-fails- with-23. 내 코드에서 CUDA 리소스를 등록 할 때 피치를 설정했지만'nvEncEncodePicture() '를 호출 할 때'inputPitch'를 설정하지 않았습니다. 제 경우에는 그 설정이 제로 였기 때문에 제게는 효과가 있었지만 2560 이하의 피치 (이것은 NV12 형식을 사용했기 때문에 받아 들일 수있었습니다)에서만 사용되었습니다. – JPNotADragon