2016-07-29 3 views
0

У меня есть один вопрос, связанный с API Nvidias NVenc. Я хочу использовать API для кодирования некоторой графики OpenGL. Моя проблема заключается в том, что API не сообщает об ошибках в течение всей программы, все кажется прекрасным. Но сгенерированный вывод не читается, например. VLC. Если я попытаюсь воспроизвести сгенерированный файл, VLC начнет мигать черным экраном около 0,5 с, а затем закончит воспроизведение. Видео имеет длину 0, размер Vid также кажется небольшим. Разрешение составляет 1280 * 720, а размер записи 5 секунд составляет всего 700 кб. Это реалистично?Выходные данные NVencs Битовый поток не читается

Поток приложения выглядит следующим образом:

  1. отдавайте вторичного FrameBuffer
  2. загрузки FrameBuffer к одному из двух ОПО (glReadPixels())
  3. Карта ПБО предыдущего кадра, чтобы получить указатель, понятный Cuda.
  4. Позвоните простому CudaKernel, преобразующему OpenGLs RGBA в ARGB, который должен быть понятен NVenc в соответствии с this (стр.18). Ядро считывает содержимое PBO и записывает преобразованное содержимое в CudaArray (созданный cudaMalloc), который зарегистрирован как InputResource с NVenc.
  5. Содержимое преобразованного массива кодируется. Событие завершения плюс соответствующий выходной бит бит-буфера попадают в очередь.
  6. Вторичный поток прослушивает выходные события очереди, если одно событие сигнализируется, выходной бит-бит получает отображение и записывается в hdd.

initializion из NVenc-кодировщик:

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); 

Encoding

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

Вы не даете много деталей, но это звучит как обычная проблема для VLC при работе с RAW-битами: VLC не может их воспроизвести, если не сообщается кодеку. Для этого попробуйте дать правильному концу файл, например. «filename.h264» для кодека 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

С помощью hall822 с форумов nvidia мне удалось решить проблему.

Основная ошибка заключалась в том, что я зарегистрировал свой ресурс 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-Frames, правильный шаг должен быть FRAME_WIDTH * 4.

Вторая ошибка заключалась в том, что мои цветовые каналы были неправильными (см. Пункт 4 в моем открытии).NVidia enum говорит, что кодировщик ожидает каналы в формате ARGB, но на самом деле это BGRA, поэтому альфа-канал, который всегда 255 загрязняет синий канал.

Редактировать: Возможно, это связано с тем, что NVidia использует внутреннюю часть внутренней части. Я пишу мои данные пикселов в массив байтов, выбирая другой тип, такой как int32, позволяет передавать фактические данные ARGB.

+0

Это очень интересно, потому что это может быть реальный ответ на проблему, которую я написал, и я ответил (или думал, что я ответил) на SO: http://stackoverflow.com/questions/32924056/nvencregisterresource-fails- с-23. В моем коде я установил поле при регистрации моего ресурса CUDA, но я никогда не пробовал устанавливать 'inputPitch' при вызове' nvEncEncodePicture() '. В моем случае эта настройка осталась на нуле, которая работала для меня, но только для смол <= 2560 (это было приемлемо для меня, потому что я использовал формат NV12). – JPNotADragon