2016-07-29 8 views
0

Ich habe eine Frage im Zusammenhang mit Nvidias NVenc API. Ich möchte die API verwenden, um einige OpenGL-Grafiken zu kodieren. Mein Problem ist, dass die API während des gesamten Programms keinen Fehler meldet, alles scheint in Ordnung zu sein. Die erzeugte Ausgabe ist jedoch z. B. nicht lesbar. VLC. Wenn ich versuche, die generierte Datei abzuspielen, würde VLC für etwa 0,5 Sekunden einen schwarzen Bildschirm blinken lassen und dann die Wiedergabe beenden. Das Video hat die Länge 0, die Größe des Vid scheint auch ziemlich klein zu sein. Auflösung ist 1280 * 720 und die Größe von 5 Sekunden Aufnahme ist nur 700kb. Ist das realistisch?NVencs Output Bitstream ist nicht lesbar

Der Ablauf der Anwendung ist wie folgt:

  1. Render zur sekundären Framebuffer
  2. Herunterladen Framebuffer zu einem von zwei PBOs (glReadPixels())
  3. die PBO des vorherigen Rahmens Karte, zu einen Zeiger von Cuda verstehen lassen.
  4. Rufen Sie einen einfachen CudaKernel, der OpenGLs RGBA in ARGB konvertiert, was von NVenc gemäß this (S.18) verständlich sein sollte. Der Kernel liest den Inhalt des PBO und schreibt den konvertierten Inhalt in ein CudaArray (erstellt mit cudaMalloc), das als InputResource mit NVenc registriert ist.
  5. Der Inhalt des konvertierten Arrays wird codiert. Ein Abschlussereignis und der entsprechende Ausgabe-Bitstream-Puffer werden in die Warteschlange gestellt.
  6. Ein sekundärer Thread überwacht die Ereignisse in der Warteschlange, wenn ein Ereignis signalisiert wird, wird der Ausgabe-Bitstream zugeordnet und auf die Festplatte geschrieben.

Die initializion von 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 

Registrierung von 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 

Jeder kleine Hinweis, wo zu sehen ist, wird sehr geschätzt. Mir gehen die Ideen aus, was falsch sein könnte.

Vielen Dank!

+0

Sie geben nicht viele Details, aber klingt wie ein allgemeines Problem für VLC im Umgang mit RAW Bitstreams: VLC kann sie nicht wiedergeben, wenn es nicht den Codec mitgeteilt wird. Versuchen Sie dazu, der Datei das richtige Ende zu geben, z. "Dateiname.h264" für h264-Codec. – kunzmi

+0

Okay, wenn ich das mache, bekomme ich folgendes Ergebnis: [click] (https://s31.postimg.org/vvgbiqg0b/Encoded_File.png). – Christoph

+0

Es scheint, dass einige Probleme im [Cross-Posting] (https://devtalk.nvidia.com/default/topic/953041/gpu-accelerated-libraries/nvencs-output-bitstream-is-not-readable) behoben wurden /). –

Antwort

1

Mit Hilfe von hall822 aus den NVIDIA Foren konnte ich das Problem lösen.

Der primäre Fehler war, dass ich meine Cuda-Ressource mit einer Teilung registriert, die der Größe des Rahmens entspricht. Ich verwende einen Framebuffer-Renderbuffer, um meinen Inhalt zu zeichnen. Die Daten davon sind ein einfaches, nicht scharfes Array. Mein erster Gedanke, eine Tonhöhe gleich Null zu geben, ist gescheitert. Der Encoder hat nichts gemacht. Die nächste Idee war, es auf die Breite des Rahmens zu setzen, ein Viertel des Bildes wurde codiert.

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

Um diese Frage zu beantworten: Ja, es ist richtig. Aber die Tonhöhe wird in Byte gemessen. Da ich RGBA-Frames kodiere, muss die korrekte Tonhöhe sein.

Der zweite Fehler war, dass meine Farbkanäle nicht richtig waren (Siehe Punkt 4 in meinem Eröffnungsbeitrag).Die NVidia enum sagt, dass der Encoder die Kanäle im ARGB-Format erwartet, aber tatsächlich ist BGRA, also der Alpha-Kanal, der immer den blauen Kanal 255 verschmutzt.

Edit: Dies kann aufgrund der Tatsache sein, dass NVidia intern kleine Endian verwendet. Ich schreibe meine Pixeldaten in eine Byte-Array, wählen Sie einen anderen Typ wie Int32 können Sie tatsächlich ARGB Daten übergeben.

+0

Das ist sehr interessant, denn es könnte die wirkliche Antwort auf ein Problem sein, das ich hatte und das ich gepostet und beantwortet (oder gedacht habe, dass ich geantwortet habe) auf SO: http://stackoverflow.com/questions/32924056/nvencregisterresource-fails- mit-23. In meinem Code stelle ich die Tonhöhe beim Registrieren meiner CUDA-Ressource ein, aber ich habe nie versucht, 'inputPitch' beim Aufruf von' nvEncEncodePicture() 'zu setzen. In meinem Fall blieb diese Einstellung also bei Null, was für mich funktionierte, aber nur für Tonhöhen <= 2560 (das war für mich akzeptabel, weil ich das NV12-Format verwendete). – JPNotADragon