Skip to content

Commit

Permalink
Fix a reference leak of hw_frames_ctx and prepare for QSV (LizardByte…
Browse files Browse the repository at this point in the history
  • Loading branch information
cgutman authored Jan 10, 2023
1 parent 5477f58 commit 44ad28e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 69 deletions.
9 changes: 8 additions & 1 deletion src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

struct sockaddr;
struct AVFrame;
struct AVBufferRef;
struct AVHWFramesContext;

// Forward declarations of boost classes to avoid having to include boost headers
// here, which results in issues with Windows.h and WinSock2.h include order.
Expand Down Expand Up @@ -196,13 +198,18 @@ struct hwdevice_t {
/**
* implementations must take ownership of 'frame'
*/
virtual int set_frame(AVFrame *frame) {
virtual int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?";
return -1;
};

virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {};

/**
* Implementations may set parameters during initialization of the hwframes context
*/
virtual void init_hwframes(AVHWFramesContext *frames) {};

virtual ~hwdevice_t() = default;
};

Expand Down
17 changes: 9 additions & 8 deletions src/platform/linux/cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,21 @@ class cuda_t : public platf::hwdevice_t {
return 0;
}

int set_frame(AVFrame *frame) override {
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
this->hwframe.reset(frame);
this->frame = frame;

auto hwframe_ctx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
auto hwframe_ctx = (AVHWFramesContext *)hw_frames_ctx->data;
if(hwframe_ctx->sw_format != AV_PIX_FMT_NV12) {
BOOST_LOG(error) << "cuda::cuda_t doesn't support any format other than AV_PIX_FMT_NV12"sv;
return -1;
}

if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) {
BOOST_LOG(error) << "Couldn't get hwframe for NVENC"sv;

return -1;
if(!frame->buf[0]) {
if(av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
BOOST_LOG(error) << "Couldn't get hwframe for NVENC"sv;
return -1;
}
}

auto cuda_ctx = (AVCUDADeviceContext *)hwframe_ctx->device_ctx->hwctx;
Expand Down Expand Up @@ -180,8 +181,8 @@ class cuda_ram_t : public cuda_t {
return sws.load_ram(img, tex.array) || sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(tex), stream.get());
}

int set_frame(AVFrame *frame) {
if(cuda_t::set_frame(frame)) {
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
if(cuda_t::set_frame(frame, hw_frames_ctx)) {
return -1;
}

Expand Down
11 changes: 6 additions & 5 deletions src/platform/linux/vaapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,15 @@ class va_t : public platf::hwdevice_t {
return 0;
}

int set_frame(AVFrame *frame) override {
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
this->hwframe.reset(frame);
this->frame = frame;

if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) {
BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv;

return -1;
if(!frame->buf[0]) {
if(av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv;
return -1;
}
}

va::DRMPRIMESurfaceDescriptor prime;
Expand Down
2 changes: 1 addition & 1 deletion src/platform/macos/nv12_zero_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int nv12_zero_device::convert(platf::img_t &img) {
return result > 0 ? 0 : -1;
}

int nv12_zero_device::set_frame(AVFrame *frame) {
int nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
this->frame = frame;

av_frame.reset(frame);
Expand Down
2 changes: 1 addition & 1 deletion src/platform/macos/nv12_zero_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class nv12_zero_device : public hwdevice_t {
int init(void *display, resolution_fn_t resolution_fn, pixel_format_fn_t pixel_format_fn);

int convert(img_t &img);
int set_frame(AVFrame *frame);
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx);
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
};

Expand Down
87 changes: 52 additions & 35 deletions src/platform/windows/display_vram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,63 @@ class hwdevice_t : public platf::hwdevice_t {
this->color_matrix = std::move(color_matrix);
}

int set_frame(AVFrame *frame) {
void init_hwframes(AVHWFramesContext *frames) override {
// We may be called with a QSV or D3D11VA context
if(frames->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
auto d3d11_frames = (AVD3D11VAFramesContext *)frames->hwctx;

// The encoder requires textures with D3D11_BIND_RENDER_TARGET set
d3d11_frames->BindFlags = D3D11_BIND_RENDER_TARGET;
d3d11_frames->MiscFlags = 0;
}

// We require a single texture
frames->initial_pool_size = 1;
}

int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
this->hwframe.reset(frame);
this->frame = frame;

// Populate this frame with a hardware buffer if one isn't there already
if(!frame->buf[0]) {
auto err = av_hwframe_get_buffer(hw_frames_ctx, frame, 0);
if(err) {
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
BOOST_LOG(error) << "Failed to get hwframe buffer: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
return -1;
}
}

// If this is a frame from a derived context, we'll need to map it to D3D11
ID3D11Texture2D *frame_texture;
if(frame->format != AV_PIX_FMT_D3D11) {
frame_t d3d11_frame { av_frame_alloc() };

d3d11_frame->format = AV_PIX_FMT_D3D11;

auto err = av_hwframe_map(d3d11_frame.get(), frame, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
if(err) {
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
BOOST_LOG(error) << "Failed to map D3D11 frame: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
return -1;
}

// Get the texture from the mapped frame
frame_texture = (ID3D11Texture2D *)d3d11_frame->data[0];
}
else {
// Otherwise, we can just use the texture inside the original frame
frame_texture = (ID3D11Texture2D *)frame->data[0];
}

auto out_width = frame->width;
auto out_height = frame->height;

float in_width = img.display->width;
float in_height = img.display->height;

// // Ensure aspect ratio is maintained
// Ensure aspect ratio is maintained
auto scalar = std::fminf(out_width / in_width, out_height / in_height);
auto out_width_f = in_width * scalar;
auto out_height_f = in_height * scalar;
Expand All @@ -414,21 +460,9 @@ class hwdevice_t : public platf::hwdevice_t {
outY_view = D3D11_VIEWPORT { offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f };
outUV_view = D3D11_VIEWPORT { offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f };

D3D11_TEXTURE2D_DESC t {};
t.Width = out_width;
t.Height = out_height;
t.MipLevels = 1;
t.ArraySize = 1;
t.SampleDesc.Count = 1;
t.Usage = D3D11_USAGE_DEFAULT;
t.Format = format;
t.BindFlags = D3D11_BIND_RENDER_TARGET;

auto status = device->CreateTexture2D(&t, nullptr, &img.encoder_texture);
if(FAILED(status)) {
BOOST_LOG(error) << "Failed to create render target texture [0x"sv << util::hex(status).to_string_view() << ']';
return -1;
}
// The underlying frame pool owns the texture, so we must reference it for ourselves
frame_texture->AddRef();
img.encoder_texture.reset(frame_texture);

img.width = out_width;
img.height = out_height;
Expand All @@ -449,7 +483,7 @@ class hwdevice_t : public platf::hwdevice_t {
D3D11_RTV_DIMENSION_TEXTURE2D
};

status = device->CreateRenderTargetView(img.encoder_texture.get(), &nv12_rt_desc, &nv12_Y_rt);
auto status = device->CreateRenderTargetView(img.encoder_texture.get(), &nv12_rt_desc, &nv12_Y_rt);
if(FAILED(status)) {
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
return -1;
Expand All @@ -463,23 +497,6 @@ class hwdevice_t : public platf::hwdevice_t {
return -1;
}

// Need to have something refcounted
if(!frame->buf[0]) {
frame->buf[0] = av_buffer_allocz(sizeof(AVD3D11FrameDescriptor));
}

auto desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
desc->texture = (ID3D11Texture2D *)img.data;
desc->index = 0;

frame->data[0] = img.data;
frame->data[1] = 0;

frame->linesize[0] = img.row_pitch;

frame->height = img.height;
frame->width = img.width;

return 0;
}

Expand Down
33 changes: 15 additions & 18 deletions src/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ util::Either<buffer_t, int> dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_c
util::Either<buffer_t, int> vaapi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx);
util::Either<buffer_t, int> cuda_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx);

int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format);
int hwframe_ctx(ctx_t &ctx, platf::hwdevice_t *hwdevice, buffer_t &hwdevice_ctx, AVPixelFormat format);

class swdevice_t : public platf::hwdevice_t {
public:
Expand Down Expand Up @@ -116,17 +116,16 @@ class swdevice_t : public platf::hwdevice_t {
return 0;
}

int set_frame(AVFrame *frame) {
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
this->frame = frame;

// If it's a hwframe, allocate buffers for hardware
if(frame->hw_frames_ctx) {
if(hw_frames_ctx) {
hw_frame.reset(frame);

if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) return -1;
if(av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) return -1;
}

if(!frame->hw_frames_ctx) {
else {
sw_frame.reset(frame);
}

Expand Down Expand Up @@ -181,9 +180,9 @@ class swdevice_t : public platf::hwdevice_t {
return 0;
}

int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format) {
int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format, bool hardware) {
// If the device used is hardware, yet the image resides on main memory
if(frame->hw_frames_ctx) {
if(hardware) {
sw_frame.reset(av_frame_alloc());

sw_frame->width = frame->width;
Expand Down Expand Up @@ -981,7 +980,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
}

hwdevice_ctx = std::move(buf_or_error.left());
if(hwframe_ctx(ctx, hwdevice_ctx, sw_fmt)) {
if(hwframe_ctx(ctx, hwdevice.get(), hwdevice_ctx, sw_fmt)) {
return std::nullopt;
}

Expand Down Expand Up @@ -1063,17 +1062,12 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
frame->width = ctx->width;
frame->height = ctx->height;


if(hardware) {
frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx);
}

std::shared_ptr<platf::hwdevice_t> device;

if(!hwdevice->data) {
auto device_tmp = std::make_unique<swdevice_t>();

if(device_tmp->init(width, height, frame.get(), sw_fmt)) {
if(device_tmp->init(width, height, frame.get(), sw_fmt, hardware)) {
return std::nullopt;
}

Expand All @@ -1083,7 +1077,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
device = std::move(hwdevice);
}

if(device->set_frame(frame.release())) {
if(device->set_frame(frame.release(), ctx->hw_frames_ctx)) {
return std::nullopt;
}

Expand Down Expand Up @@ -1812,8 +1806,8 @@ int init() {
return 0;
}

int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) };
int hwframe_ctx(ctx_t &ctx, platf::hwdevice_t *hwdevice, buffer_t &hwdevice_ctx, AVPixelFormat format) {
buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice_ctx.get()) };

auto frame_ctx = (AVHWFramesContext *)frame_ref->data;
frame_ctx->format = ctx->pix_fmt;
Expand All @@ -1822,6 +1816,9 @@ int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
frame_ctx->width = ctx->width;
frame_ctx->initial_pool_size = 0;

// Allow the hwdevice to modify hwframe context parameters
hwdevice->init_hwframes(frame_ctx);

if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) {
return err;
}
Expand Down

0 comments on commit 44ad28e

Please sign in to comment.