Выходной битовый поток NVencs не читается

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

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

  1. Визуализировать во вторичный фреймбуфер
  2. Загрузите Framebuffer в одно из двух PBO (glReadPixels ())
  3. Отобразите PBO предыдущего кадра, чтобы получить указатель, понятный Cuda.
  4. Назовите простой CudaKernel, конвертирующий RGBA OpenGL в ARGB, который должен быть понятен NVenc согласно этот(С.18). Ядро считывает содержимое PBO и записывает преобразованный контент в CudaArray (созданный с помощью cudaMalloc), который зарегистрирован как InputResource в NVenc.
  5. Содержимое преобразованного массива кодируется. Событие завершения плюс соответствующий выходной буфер потока битов ставятся в очередь.
  6. Вторичный поток прослушивает выходные события в очереди. Если сигнализируется одно событие, выходной битовый поток отображается и записывается в жесткий диск.

Инициализация NVenc-Encoder:

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

Решение

С помощью 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-кадры, правильный шаг должен быть FRAME_WIDTH * 4,

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

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

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]