|
|
@@ -1,5 +1,6 @@
|
|
|
-#include "third_party/scope_guard.hpp"
|
|
|
#include "encoder_nvenc.h"
|
|
|
+#include "third_party/scope_guard.hpp"
|
|
|
+#include "image_process_v5/image_process.h"
|
|
|
|
|
|
#include <nvEncodeAPI.h>
|
|
|
|
|
|
@@ -39,15 +40,18 @@ struct encoder_nvenc::impl {
|
|
|
void *encoder;
|
|
|
NV_ENC_OUTPUT_PTR output_buf;
|
|
|
|
|
|
- cv::Size frame_size;
|
|
|
+ cv::Size frame_size; // as normal image
|
|
|
+ int currant_frame_rate = 0;
|
|
|
+ float current_bitrate_mbps = 0;
|
|
|
+ uint64_t last_frame_id = 0;
|
|
|
+
|
|
|
FILE *save_file = nullptr;
|
|
|
bool save_length;
|
|
|
|
|
|
- void *last_frame_ptr = nullptr;
|
|
|
- NV_ENC_REGISTERED_PTR last_reg_ptr = nullptr;
|
|
|
- uint64_t last_frame_id = 0;
|
|
|
-
|
|
|
- smart_cuda_stream *stream = nullptr;
|
|
|
+ static constexpr auto frame_format = NV_ENC_BUFFER_FORMAT_NV12;
|
|
|
+ void *frame_ptr = nullptr; // store frame data
|
|
|
+ size_t frame_pitch = 0;
|
|
|
+ NV_ENC_REGISTERED_PTR frame_reg_ptr = nullptr;
|
|
|
|
|
|
~impl() {
|
|
|
// notify the end of stream
|
|
|
@@ -58,6 +62,7 @@ struct encoder_nvenc::impl {
|
|
|
// releasing resources
|
|
|
unregister_frame_ptr();
|
|
|
API_CHECK(api->nvEncDestroyBitstreamBuffer(encoder, output_buf));
|
|
|
+ CUDA_API_CHECK(cudaFree(frame_ptr));
|
|
|
|
|
|
// close encoder
|
|
|
API_CHECK(api->nvEncDestroyEncoder(encoder));
|
|
|
@@ -78,13 +83,14 @@ struct encoder_nvenc::impl {
|
|
|
API_CHECK_P(NvEncodeAPICreateInstance(api.get()));
|
|
|
}
|
|
|
|
|
|
- // get cuda context
|
|
|
- auto cuda_ctx = conf.ctx;
|
|
|
+ // initialize CUDA
|
|
|
+ current_cuda_stream();
|
|
|
|
|
|
// create encoder
|
|
|
auto ret = new impl;
|
|
|
- ret->stream = conf.stream;
|
|
|
ret->frame_size = conf.frame_size;
|
|
|
+ ret->currant_frame_rate = conf.frame_rate;
|
|
|
+ ret->current_bitrate_mbps = conf.bitrate_mbps;
|
|
|
auto closer = sg::make_scope_guard([&] {
|
|
|
if (ret->save_file != nullptr) {
|
|
|
fclose(ret->save_file);
|
|
|
@@ -94,7 +100,7 @@ struct encoder_nvenc::impl {
|
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = {
|
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER};
|
|
|
session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
|
|
- session_params.device = *cuda_ctx;
|
|
|
+ session_params.device = cuda_ctx;
|
|
|
session_params.apiVersion = NVENCAPI_VERSION;
|
|
|
API_CHECK_P(api->nvEncOpenEncodeSessionEx(&session_params, &ret->encoder));
|
|
|
|
|
|
@@ -135,6 +141,12 @@ struct encoder_nvenc::impl {
|
|
|
// init_params.bufferFormat = frame_buffer_type; // ignored as document say only DX12 cares it
|
|
|
API_CHECK_P(api->nvEncInitializeEncoder(ret->encoder, &init_params));
|
|
|
|
|
|
+ // create input frame buffer
|
|
|
+ auto input_size = normal_size_to_nv12(conf.frame_size);
|
|
|
+ CUDA_API_CHECK(cudaMallocPitch(&ret->frame_ptr, &ret->frame_pitch,
|
|
|
+ input_size.width, input_size.height));
|
|
|
+ ret->register_frame_ptr();
|
|
|
+
|
|
|
// create output buffer
|
|
|
NV_ENC_CREATE_BITSTREAM_BUFFER buffer_config = {
|
|
|
NV_ENC_CREATE_BITSTREAM_BUFFER_VER};
|
|
|
@@ -156,58 +168,59 @@ struct encoder_nvenc::impl {
|
|
|
}
|
|
|
|
|
|
void unregister_frame_ptr() {
|
|
|
- if (last_reg_ptr == nullptr) return;
|
|
|
- API_CHECK(api->nvEncUnregisterResource(encoder, last_reg_ptr));
|
|
|
- last_reg_ptr = nullptr;
|
|
|
- }
|
|
|
-
|
|
|
- static NV_ENC_BUFFER_FORMAT get_buffer_format(const image_ptr &img) {
|
|
|
- if (img->pixel_format() == PIX_NV12) {
|
|
|
- assert(img->cv_type() == CV_8UC1);
|
|
|
- return NV_ENC_BUFFER_FORMAT_NV12;
|
|
|
- } else if (img->pixel_format() == PIX_NORMAL) {
|
|
|
- assert(img->cv_type() == CV_8UC4);
|
|
|
- return NV_ENC_BUFFER_FORMAT_ARGB;
|
|
|
- }
|
|
|
- RET_ERROR_E;
|
|
|
+ API_CHECK(api->nvEncUnregisterResource(encoder, frame_reg_ptr));
|
|
|
}
|
|
|
|
|
|
- void register_frame_ptr(const image_memory &mem) {
|
|
|
+ void register_frame_ptr() {
|
|
|
NV_ENC_REGISTER_RESOURCE reg_params = {NV_ENC_REGISTER_RESOURCE_VER};
|
|
|
reg_params.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
|
|
|
- reg_params.width = mem.img->width();
|
|
|
- reg_params.height = mem.img->height();
|
|
|
- reg_params.pitch = mem.pitch;
|
|
|
- reg_params.resourceToRegister = mem.start_ptr();
|
|
|
- reg_params.bufferFormat = get_buffer_format(mem.img);
|
|
|
+ reg_params.width = frame_size.width;
|
|
|
+ reg_params.height = frame_size.height;
|
|
|
+ reg_params.pitch = frame_pitch;
|
|
|
+ reg_params.resourceToRegister = frame_ptr;
|
|
|
+ reg_params.bufferFormat = frame_format;
|
|
|
reg_params.bufferUsage = NV_ENC_INPUT_IMAGE;
|
|
|
API_CHECK(api->nvEncRegisterResource(encoder, ®_params));
|
|
|
- last_reg_ptr = reg_params.registeredResource;
|
|
|
+ frame_reg_ptr = reg_params.registeredResource;
|
|
|
+ }
|
|
|
+
|
|
|
+ void upload_frame(sp_image img) {
|
|
|
+ if (img.cv_type() == CV_8UC3) {
|
|
|
+ img = image_rgb_to_nv12(img);
|
|
|
+ }
|
|
|
+ assert(img.cv_type() == CV_8UC1);
|
|
|
+ assert(img.cv_size() == normal_size_to_nv12(frame_size));
|
|
|
+
|
|
|
+ auto stream_guard = cuda_stream_guard(cudaStreamLegacy);
|
|
|
+ auto read_helper = read_access_helper(img.cuda());
|
|
|
+ CUDA_API_CHECK(cudaMemcpy2D(frame_ptr, frame_pitch, read_helper.ptr(), img.pitch(),
|
|
|
+ img.byte_width(), img.height(), cudaMemcpyDeviceToDevice));
|
|
|
}
|
|
|
|
|
|
- frame_info encode(const image_ptr &img, bool force_idr = false) {
|
|
|
- // register pointer if needed
|
|
|
- auto mem = img->memory(MEM_CUDA, stream);
|
|
|
- auto buffer_fmt = get_buffer_format(img);
|
|
|
- // TODO: image pointer may change frequently
|
|
|
- if (mem.start_ptr() != last_frame_ptr) [[unlikely]] {
|
|
|
- assert(img->size() == frame_size);
|
|
|
- unregister_frame_ptr();
|
|
|
- register_frame_ptr(mem);
|
|
|
+ frame_info encode(const sp_image& img, const bool force_idr = false) {
|
|
|
+ // adjust frame rate
|
|
|
+ int frame_rate = img.query_meta<float>("frame_rate");
|
|
|
+ if (frame_rate != currant_frame_rate) {
|
|
|
+ auto mod_conf = modifiable_config();
|
|
|
+ mod_conf.frame_rate = frame_rate;
|
|
|
+ mod_conf.bitrate_mbps = current_bitrate_mbps;
|
|
|
+ change_config(mod_conf);
|
|
|
}
|
|
|
|
|
|
+ upload_frame(img);
|
|
|
+
|
|
|
// map input resource
|
|
|
NV_ENC_MAP_INPUT_RESOURCE map_params = {
|
|
|
NV_ENC_MAP_INPUT_RESOURCE_VER};
|
|
|
- map_params.registeredResource = last_reg_ptr;
|
|
|
+ map_params.registeredResource = frame_reg_ptr;
|
|
|
API_CHECK(api->nvEncMapInputResource(encoder, &map_params));
|
|
|
- assert(map_params.mappedBufferFmt == buffer_fmt);
|
|
|
+ assert(map_params.mappedBufferFmt == frame_format);
|
|
|
|
|
|
// encode frame
|
|
|
NV_ENC_PIC_PARAMS pic_params = {NV_ENC_PIC_PARAMS_VER};
|
|
|
- pic_params.inputWidth = img->width();
|
|
|
- pic_params.inputHeight = mem.height;
|
|
|
- pic_params.inputPitch = mem.pitch;
|
|
|
+ pic_params.inputWidth = frame_size.width;
|
|
|
+ pic_params.inputHeight = frame_size.height;
|
|
|
+ pic_params.inputPitch = frame_pitch;
|
|
|
if (force_idr) { // request for IDR frame
|
|
|
pic_params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS;
|
|
|
} else {
|
|
|
@@ -215,7 +228,7 @@ struct encoder_nvenc::impl {
|
|
|
}
|
|
|
pic_params.inputBuffer = map_params.mappedResource;
|
|
|
pic_params.outputBitstream = output_buf;
|
|
|
- pic_params.bufferFmt = buffer_fmt;
|
|
|
+ pic_params.bufferFmt = frame_format;
|
|
|
pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; // TODO; learn more about this
|
|
|
API_CHECK(api->nvEncEncodePicture(encoder, &pic_params));
|
|
|
|
|
|
@@ -248,6 +261,9 @@ struct encoder_nvenc::impl {
|
|
|
}
|
|
|
|
|
|
void change_config(modifiable_config conf) {
|
|
|
+ if (conf.frame_rate == 0) {
|
|
|
+ conf.frame_rate = currant_frame_rate;
|
|
|
+ }
|
|
|
NV_ENC_RECONFIGURE_PARAMS params = {NV_ENC_RECONFIGURE_PARAMS_VER};
|
|
|
init_params->frameRateNum = conf.frame_rate;
|
|
|
init_params->encodeConfig->rcParams.averageBitRate = conf.bitrate_mbps * 1e6;
|
|
|
@@ -255,6 +271,8 @@ struct encoder_nvenc::impl {
|
|
|
params.resetEncoder = true;
|
|
|
params.forceIDR = true;
|
|
|
API_CHECK(api->nvEncReconfigureEncoder(encoder, ¶ms));
|
|
|
+ currant_frame_rate = conf.frame_rate;
|
|
|
+ current_bitrate_mbps = conf.bitrate_mbps;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -268,11 +286,7 @@ encoder_nvenc::pointer encoder_nvenc::create(create_config conf) {
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-frame_info encoder_nvenc::encode(const image_u8c4 &img, bool force_idr) {
|
|
|
- return encode(create_image(img), force_idr);
|
|
|
-}
|
|
|
-
|
|
|
-frame_info encoder_nvenc::encode(const image_ptr &img, bool force_idr) {
|
|
|
+frame_info encoder_nvenc::encode(const sp_image &img, const bool force_idr) const {
|
|
|
return pimpl->encode(img, force_idr);
|
|
|
}
|
|
|
|