* [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
@ 2025-05-22 13:20 Dash Santosh Sathyanarayanan
2025-05-22 15:25 ` Timo Rothenpieler
0 siblings, 1 reply; 5+ messages in thread
From: Dash Santosh Sathyanarayanan @ 2025-05-22 13:20 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Sachin Prakash Kumar
[-- Attachment #1: Type: text/plain, Size: 23241 bytes --]
This commit introduces a new hardware-accelerated video filter, scale_d3d11,
which performs scaling and format conversion using Direct3D 11. The filter enables
efficient GPU-based scaling and pixel format conversion (p010 to nv12), reducing
CPU overhead and latency in video pipelines.
---
Changelog | 1 +
libavcodec/decode.c | 2 +-
libavcodec/dxva2.c | 3 +
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_scale_d3d11.c | 480 ++++++++++++++++++++++++++++++++++
libavutil/hwcontext_d3d11va.c | 40 ++-
7 files changed, 514 insertions(+), 14 deletions(-)
create mode 100644 libavfilter/vf_scale_d3d11.c
diff --git a/Changelog b/Changelog
index 4217449438..68610a63d0 100644
--- a/Changelog
+++ b/Changelog
@@ -18,6 +18,7 @@ version <next>:
- APV encoding support through a libopenapv wrapper
- VVC decoder supports all content of SCC (Screen Content Coding):
IBC (Inter Block Copy), Palette Mode and ACT (Adaptive Color Transform
+- vf_scale_d3d11 filter
version 7.1:
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index c2b2dd6e3b..a796ae7930 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1079,7 +1079,7 @@ int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx,
if (frames_ctx->initial_pool_size) {
// We guarantee 4 base work surfaces. The function above guarantees 1
// (the absolute minimum), so add the missing count.
- frames_ctx->initial_pool_size += 3;
+ frames_ctx->initial_pool_size += 33;
}
ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
index 22ecd5acaf..37dab6cd68 100644
--- a/libavcodec/dxva2.c
+++ b/libavcodec/dxva2.c
@@ -647,6 +647,9 @@ int ff_dxva2_common_frame_params(AVCodecContext *avctx,
AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
frames_hwctx->BindFlags |= D3D11_BIND_DECODER;
+ if (frames_ctx->sw_format == AV_PIX_FMT_NV12) {
+ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER;
+ }
}
#endif
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0effe4127f..dc46e946f2 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -461,6 +461,7 @@ OBJS-$(CONFIG_ROBERTS_OPENCL_FILTER) += vf_convolution_opencl.o opencl.o
OBJS-$(CONFIG_ROTATE_FILTER) += vf_rotate.o
OBJS-$(CONFIG_SAB_FILTER) += vf_sab.o
OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale_eval.o framesync.o
+OBJS-$(CONFIG_SCALE_D3D11_FILTER) += vf_scale_d3d11.o scale_eval.o
OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o scale_eval.o \
vf_scale_cuda.ptx.o cuda/load_helper.o
OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale_eval.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5ea33cdf01..a20fff57e7 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -435,6 +435,7 @@ extern const FFFilter ff_vf_scale;
extern const FFFilter ff_vf_vpp_amf;
extern const FFFilter ff_vf_sr_amf;
extern const FFFilter ff_vf_scale_cuda;
+extern const FFFilter ff_vf_scale_d3d11;
extern const FFFilter ff_vf_scale_npp;
extern const FFFilter ff_vf_scale_qsv;
extern const FFFilter ff_vf_scale_vaapi;
diff --git a/libavfilter/vf_scale_d3d11.c b/libavfilter/vf_scale_d3d11.c
new file mode 100644
index 0000000000..e079424002
--- /dev/null
+++ b/libavfilter/vf_scale_d3d11.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2025 MulticorewWare, Inc.
+ *
+ * Authors: Dash Santosh <dash.sathanatayanan@multicorewareinc.com>
+ * Sachin <sachin.prakash@multicorewareinc.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "compat/w32dlfcn.h"
+
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_d3d11va.h"
+
+#include "filters.h"
+#include "scale_eval.h"
+#include "video.h"
+
+enum OutputFormat {
+ OUTPUT_NV12 = 0,
+ OUTPUT_P010 = 1,
+};
+
+typedef struct ScaleD3D11Context {
+ const AVClass *classCtx;
+ char *w_expr;
+ char *h_expr;
+ int output_format_opt;
+
+ ///< D3D11 objects
+ ID3D11Device *device;
+ ID3D11DeviceContext *context;
+ ID3D11VideoDevice *videoDevice;
+ ID3D11VideoProcessor *processor;
+ ID3D11VideoProcessorEnumerator *enumerator;
+ ID3D11VideoProcessorOutputView *outputView;
+ ID3D11VideoProcessorInputView *inputView;
+
+ ///< Buffer references
+ AVBufferRef *hw_device_ctx;
+ AVBufferRef *hw_frames_ctx_out;
+
+ ///< Dimensions and formats
+ int width, height;
+ int inputWidth, inputHeight;
+ DXGI_FORMAT input_format;
+ DXGI_FORMAT output_format;
+} ScaleD3D11Context;
+
+static av_cold int scale_d3d11_init(AVFilterContext *ctx) {
+ ///< all real work is done in config_props and filter_frame
+ return 0;
+}
+
+static void release_d3d11_resources(ScaleD3D11Context *s) {
+ if (s->outputView) {
+ s->outputView->lpVtbl->Release(s->outputView);
+ s->outputView = NULL;
+ }
+
+ if (s->processor) {
+ s->processor->lpVtbl->Release(s->processor);
+ s->processor = NULL;
+ }
+
+ if (s->enumerator) {
+ s->enumerator->lpVtbl->Release(s->enumerator);
+ s->enumerator = NULL;
+ }
+
+ if (s->videoDevice) {
+ s->videoDevice->lpVtbl->Release(s->videoDevice);
+ s->videoDevice = NULL;
+ }
+}
+
+static int scale_d3d11_configure_processor(ScaleD3D11Context *s, AVFilterContext *ctx) {
+ HRESULT hr;
+
+ switch (s->output_format_opt) {
+ case OUTPUT_NV12:
+ s->output_format = DXGI_FORMAT_NV12;
+ break;
+ case OUTPUT_P010:
+ s->output_format = DXGI_FORMAT_P010;
+ break;
+ default:
+ av_log(ctx, AV_LOG_ERROR, "Invalid output format specified\n");
+ return AVERROR(EINVAL);
+ }
+
+ ///< Get D3D11 device and context from hardware device context
+ AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
+ AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx;
+ s->device = d3d11_hwctx->device;
+ s->context = d3d11_hwctx->device_context;
+
+ av_log(ctx, AV_LOG_VERBOSE, "Configuring D3D11 video processor: %dx%d -> %dx%d\n",
+ s->inputWidth, s->inputHeight, s->width, s->height);
+
+ ///< Define the video processor content description
+ D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc = {
+ .InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE,
+ .InputWidth = s->inputWidth,
+ .InputHeight = s->inputHeight,
+ .OutputWidth = s->width,
+ .OutputHeight = s->height,
+ .Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL,
+ };
+
+ ///< Query video device interface
+ hr = s->device->lpVtbl->QueryInterface(s->device, &IID_ID3D11VideoDevice, (void **)&s->videoDevice);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to get D3D11 video device interface: HRESULT 0x%lX\n", hr);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Create video processor enumerator
+ hr = s->videoDevice->lpVtbl->CreateVideoProcessorEnumerator(s->videoDevice, &contentDesc, &s->enumerator);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create video processor enumerator: HRESULT 0x%lX\n", hr);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Create the video processor
+ hr = s->videoDevice->lpVtbl->CreateVideoProcessor(s->videoDevice, s->enumerator, 0, &s->processor);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create video processor: HRESULT 0x%lX\n", hr);
+ return AVERROR_EXTERNAL;
+ }
+
+ av_log(ctx, AV_LOG_VERBOSE, "D3D11 video processor successfully configured\n");
+ return 0;
+}
+
+static int scale_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ ScaleD3D11Context *s = ctx->priv;
+ AVFilterLink *outlink = ctx->outputs[0];
+ ID3D11VideoProcessorInputView *inputView = NULL;
+ ID3D11VideoContext *videoContext = NULL;
+ AVFrame *out = NULL;
+ int ret = 0;
+ HRESULT hr;
+
+ ///< Validate input frame
+ if (!in) {
+ av_log(ctx, AV_LOG_ERROR, "Null input frame\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (!in->hw_frames_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "No hardware frames context in input frame\n");
+ av_frame_free(&in);
+ return AVERROR(EINVAL);
+ }
+
+ ///< Verify hardware device contexts
+ AVHWFramesContext *frames_ctx = (AVHWFramesContext *)in->hw_frames_ctx->data;
+
+ if (!s->hw_device_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Filter hardware device context is uninitialized\n");
+ av_frame_free(&in);
+ return AVERROR(EINVAL);
+ }
+
+ AVHWDeviceContext *input_device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data;
+ AVHWDeviceContext *filter_device_ctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
+
+ if (input_device_ctx->type != filter_device_ctx->type) {
+ av_log(ctx, AV_LOG_ERROR, "Mismatch between input and filter hardware device types\n");
+ av_frame_free(&in);
+ return AVERROR(EINVAL);
+ }
+
+ ///< Allocate output frame
+ out = av_frame_alloc();
+ if (!out) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to allocate output frame\n");
+ av_frame_free(&in);
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n");
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return ret;
+ }
+
+ ///< Configure the D3D11 video processor if not already configured
+ if (!s->processor) {
+ ///< Get info from input texture
+ D3D11_TEXTURE2D_DESC textureDesc;
+ ID3D11Texture2D *input_texture = (ID3D11Texture2D *)in->data[0];
+ input_texture->lpVtbl->GetDesc(input_texture, &textureDesc);
+
+ s->inputWidth = textureDesc.Width;
+ s->inputHeight = textureDesc.Height;
+ s->input_format = textureDesc.Format;
+
+ ret = scale_d3d11_configure_processor(s, ctx);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to configure processor\n");
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return ret;
+ }
+ }
+
+ ///< Get input texture and prepare input view
+ ID3D11Texture2D *d3d11_texture = (ID3D11Texture2D *)in->data[0];
+ int subIdx = (int)(intptr_t)in->data[1];
+
+ D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputViewDesc = {
+ .FourCC = s->input_format,
+ .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
+ .Texture2D.ArraySlice = subIdx
+ };
+
+ hr = s->videoDevice->lpVtbl->CreateVideoProcessorInputView(
+ s->videoDevice, (ID3D11Resource *)d3d11_texture, s->enumerator, &inputViewDesc, &inputView);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create input view: HRESULT 0x%lX\n", hr);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Create output view for current texture
+ ID3D11Texture2D *output_texture = (ID3D11Texture2D *)out->data[0];
+ D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputViewDesc = {
+ .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
+ .Texture2D = { .MipSlice = 0 },
+ };
+
+ hr = s->videoDevice->lpVtbl->CreateVideoProcessorOutputView(
+ s->videoDevice, (ID3D11Resource *)output_texture, s->enumerator, &outputViewDesc, &s->outputView);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create output view: HRESULT 0x%lX\n", hr);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Set up processing stream
+ D3D11_VIDEO_PROCESSOR_STREAM stream = {
+ .Enable = TRUE,
+ .pInputSurface = inputView,
+ .OutputIndex = 0
+ };
+
+ ///< Get video context
+ hr = s->context->lpVtbl->QueryInterface(s->context, &IID_ID3D11VideoContext, (void **)&videoContext);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to get video context: HRESULT 0x%lX\n", hr);
+ inputView->lpVtbl->Release(inputView);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Process the frame
+ hr = videoContext->lpVtbl->VideoProcessorBlt(videoContext, s->processor, s->outputView, 0, 1, &stream);
+ if (FAILED(hr)) {
+ av_log(ctx, AV_LOG_ERROR, "VideoProcessorBlt failed: HRESULT 0x%lX\n", hr);
+ videoContext->lpVtbl->Release(videoContext);
+ inputView->lpVtbl->Release(inputView);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Set up output frame
+ ret = av_frame_copy_props(out, in);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
+ videoContext->lpVtbl->Release(videoContext);
+ inputView->lpVtbl->Release(inputView);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return ret;
+ }
+
+ out->data[0] = (uint8_t *)output_texture;
+ out->data[1] = (uint8_t *)(intptr_t)0;
+ out->width = s->width;
+ out->height = s->height;
+ out->format = AV_PIX_FMT_D3D11;
+
+ ///< Clean up resources
+ inputView->lpVtbl->Release(inputView);
+ videoContext->lpVtbl->Release(videoContext);
+ if (s->outputView) {
+ s->outputView->lpVtbl->Release(s->outputView);
+ s->outputView = NULL;
+ }
+ av_frame_free(&in);
+
+ ///< Forward the frame
+ return ff_filter_frame(outlink, out);
+}
+
+static int scale_d3d11_config_props(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ ScaleD3D11Context *s = ctx->priv;
+ AVFilterLink *inlink = ctx->inputs[0];
+ FilterLink *inl = ff_filter_link(inlink);
+ FilterLink *outl = ff_filter_link(outlink);
+ int ret;
+
+ ///< Clean up any previous resources
+ release_d3d11_resources(s);
+
+ ///< Evaluate output dimensions
+ ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &s->width, &s->height);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
+ return ret;
+ }
+
+ outlink->w = s->width;
+ outlink->h = s->height;
+
+ ///< Validate input hw_frames_ctx
+ if (!inl->hw_frames_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on input link\n");
+ return AVERROR(EINVAL);
+ }
+
+ ///< Propagate hw_frames_ctx to output
+ outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
+ if (!outl->hw_frames_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx to output\n");
+ return AVERROR(ENOMEM);
+ }
+
+ ///< Initialize filter's hardware device context
+ if (!s->hw_device_ctx) {
+ AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
+ s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
+ if (!s->hw_device_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter hardware device context\n");
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ ///< Get D3D11 device and context (but don't initialize processor yet - done in filter_frame)
+ AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
+ AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx;
+
+ s->device = d3d11_hwctx->device;
+ s->context = d3d11_hwctx->device_context;
+
+ if (!s->device || !s->context) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device or context\n");
+ return AVERROR(EINVAL);
+ }
+
+ ///< Create new hardware frames context for output
+ AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
+ s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
+ if (!s->hw_frames_ctx_out)
+ return AVERROR(ENOMEM);
+
+ enum AVPixelFormat sw_format;
+ switch (s->output_format_opt) {
+ case OUTPUT_NV12:
+ sw_format = AV_PIX_FMT_NV12;
+ break;
+ case OUTPUT_P010:
+ sw_format = AV_PIX_FMT_P010;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ AVHWFramesContext *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data;
+ frames_ctx->format = AV_PIX_FMT_D3D11;
+ frames_ctx->sw_format = sw_format;
+ frames_ctx->width = s->width;
+ frames_ctx->height = s->height;
+ frames_ctx->initial_pool_size = 30; ///< Adjust pool size as needed
+
+ AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
+ frames_hwctx->MiscFlags = 0;
+ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER;
+
+ ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
+ if (ret < 0) {
+ av_buffer_unref(&s->hw_frames_ctx_out);
+ return ret;
+ }
+
+ outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
+ if (!outl->hw_frames_ctx)
+ return AVERROR(ENOMEM);
+
+ av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale config: %dx%d -> %dx%d\n",
+ inlink->w, inlink->h, outlink->w, outlink->h);
+ return 0;
+}
+
+static av_cold void scale_d3d11_uninit(AVFilterContext *ctx) {
+ ScaleD3D11Context *s = ctx->priv;
+
+ ///< Release D3D11 resources
+ release_d3d11_resources(s);
+
+ ///< Free the hardware device context reference
+ av_buffer_unref(&s->hw_frames_ctx_out);
+ av_buffer_unref(&s->hw_device_ctx);
+
+ ///< Free option strings
+ av_freep(&s->w_expr);
+ av_freep(&s->h_expr);
+}
+
+static const AVFilterPad scale_d3d11_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = scale_d3d11_filter_frame,
+ },
+};
+
+static const AVFilterPad scale_d3d11_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = scale_d3d11_config_props,
+ },
+};
+
+#define OFFSET(x) offsetof(ScaleD3D11Context, x)
+#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
+
+static const AVOption scale_d3d11_options[] = {
+ { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
+ { "height", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
+ { "output_fmt", "Output format", OFFSET(output_format_opt), AV_OPT_TYPE_INT, {.i64 = OUTPUT_NV12}, 0, OUTPUT_P010, FLAGS, "fmt" },
+ { "nv12", "NV12 format", 0, AV_OPT_TYPE_CONST, {.i64 = OUTPUT_NV12}, 0, 0, FLAGS, "fmt" },
+ { "p010", "P010 format", 0, AV_OPT_TYPE_CONST, {.i64 = OUTPUT_P010}, 0, 0, FLAGS, "fmt" },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(scale_d3d11);
+
+const FFFilter ff_vf_scale_d3d11 = {
+ .p.name = "scale_d3d11",
+ .p.description = NULL_IF_CONFIG_SMALL("Scale video using Direct3D11"),
+ .priv_size = sizeof(ScaleD3D11Context),
+ .p.priv_class = &scale_d3d11_class,
+ .init = scale_d3d11_init,
+ .uninit = scale_d3d11_uninit,
+ FILTER_INPUTS(scale_d3d11_inputs),
+ FILTER_OUTPUTS(scale_d3d11_outputs),
+ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
+ .p.flags = AVFILTER_FLAG_HWDEVICE,
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+};
\ No newline at end of file
diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
index 1a047ce57b..36694896e4 100644
--- a/libavutil/hwcontext_d3d11va.c
+++ b/libavutil/hwcontext_d3d11va.c
@@ -82,6 +82,8 @@ typedef struct D3D11VAFramesContext {
int nb_surfaces;
int nb_surfaces_used;
+ int retries;
+ int max_retries;
DXGI_FORMAT format;
@@ -258,7 +260,9 @@ static AVBufferRef *d3d11va_pool_alloc(void *opaque, size_t size)
ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
if (s->nb_surfaces_used >= texDesc.ArraySize) {
- av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
+ if (s->retries >= s->max_retries) {
+ av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
+ }
return NULL;
}
@@ -339,20 +343,30 @@ static int d3d11va_frames_init(AVHWFramesContext *ctx)
static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
{
AVD3D11FrameDescriptor *desc;
+ D3D11VAFramesContext *s = ctx->hwctx;
+ s->retries = 0;
+ s->max_retries = 50;
+
+ while (s->retries < s->max_retries) {
+
+ frame->buf[0] = av_buffer_pool_get(ctx->pool);
+ if (frame->buf[0]) {
+ desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
+
+ frame->data[0] = (uint8_t *)desc->texture;
+ frame->data[1] = (uint8_t *)desc->index;
+ frame->format = AV_PIX_FMT_D3D11;
+ frame->width = ctx->width;
+ frame->height = ctx->height;
+
+ return 0;
+ }
- frame->buf[0] = av_buffer_pool_get(ctx->pool);
- if (!frame->buf[0])
- return AVERROR(ENOMEM);
-
- desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
-
- frame->data[0] = (uint8_t *)desc->texture;
- frame->data[1] = (uint8_t *)desc->index;
- frame->format = AV_PIX_FMT_D3D11;
- frame->width = ctx->width;
- frame->height = ctx->height;
+ av_usleep(1000);
+ s->retries++;
+ }
- return 0;
+ return AVERROR(ENOMEM);
}
static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
--
2.34.1
[-- Attachment #2: 0001-avfilter-add-scale_d3d11-filter.patch --]
[-- Type: application/octet-stream, Size: 22833 bytes --]
[-- Attachment #3: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
2025-05-22 13:20 [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter Dash Santosh Sathyanarayanan
@ 2025-05-22 15:25 ` Timo Rothenpieler
2025-05-22 17:57 ` Dash Santosh Sathyanarayanan
0 siblings, 1 reply; 5+ messages in thread
From: Timo Rothenpieler @ 2025-05-22 15:25 UTC (permalink / raw)
To: ffmpeg-devel
On 22/05/2025 15:20, Dash Santosh Sathyanarayanan wrote:
> This commit introduces a new hardware-accelerated video filter, scale_d3d11,
> which performs scaling and format conversion using Direct3D 11. The filter enables
> efficient GPU-based scaling and pixel format conversion (p010 to nv12), reducing
> CPU overhead and latency in video pipelines.
> ---
> Changelog | 1 +
> libavcodec/decode.c | 2 +-
> libavcodec/dxva2.c | 3 +
> libavfilter/Makefile | 1 +
> libavfilter/allfilters.c | 1 +
> libavfilter/vf_scale_d3d11.c | 480 ++++++++++++++++++++++++++++++++++
> libavutil/hwcontext_d3d11va.c | 40 ++-
> 7 files changed, 514 insertions(+), 14 deletions(-)
> create mode 100644 libavfilter/vf_scale_d3d11.c
>
> diff --git a/Changelog b/Changelog
> index 4217449438..68610a63d0 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -18,6 +18,7 @@ version <next>:
> - APV encoding support through a libopenapv wrapper
> - VVC decoder supports all content of SCC (Screen Content Coding):
> IBC (Inter Block Copy), Palette Mode and ACT (Adaptive Color Transform
> +- vf_scale_d3d11 filter
Bit of a nit, this could at last say "Added".
>
>
> version 7.1:
> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> index c2b2dd6e3b..a796ae7930 100644
> --- a/libavcodec/decode.c
> +++ b/libavcodec/decode.c
> @@ -1079,7 +1079,7 @@ int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx,
> if (frames_ctx->initial_pool_size) {
> // We guarantee 4 base work surfaces. The function above guarantees 1
> // (the absolute minimum), so add the missing count.
> - frames_ctx->initial_pool_size += 3;
> + frames_ctx->initial_pool_size += 33;
This seems a bit extreme, and can potentially drastically increase VRAM
usage of anything using d3d11va.
> }
>
> ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
> diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
> index 22ecd5acaf..37dab6cd68 100644
> --- a/libavcodec/dxva2.c
> +++ b/libavcodec/dxva2.c
> @@ -647,6 +647,9 @@ int ff_dxva2_common_frame_params(AVCodecContext *avctx,
> AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
>
> frames_hwctx->BindFlags |= D3D11_BIND_DECODER;
> + if (frames_ctx->sw_format == AV_PIX_FMT_NV12) {
> + frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER;
> + }
This change also seems a bit random here. Using NV12 does not
automatically mean you'll encode with it.
Did not look at the rest yet.
> + return AVERROR_EXTERNAL;
> + }
> +
> + ///< Set up output frame
> + ret = av_frame_copy_props(out, in);
> + if (ret < 0) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
> + videoContext->lpVtbl->Release(videoContext);
> + inputView->lpVtbl->Release(inputView);
> + av_frame_free(&in);
> + av_frame_free(&out);
> + return ret;
> + }
> +
> + out->data[0] = (uint8_t *)output_texture;
> + out->data[1] = (uint8_t *)(intptr_t)0;
> + out->width = s->width;
> + out->height = s->height;
> + out->format = AV_PIX_FMT_D3D11;
> +
> + ///< Clean up resources
> + inputView->lpVtbl->Release(inputView);
> + videoContext->lpVtbl->Release(videoContext);
> + if (s->outputView) {
> + s->outputView->lpVtbl->Release(s->outputView);
> + s->outputView = NULL;
> + }
> + av_frame_free(&in);
> +
> + ///< Forward the frame
> + return ff_filter_frame(outlink, out);
> +}
> +
> +static int scale_d3d11_config_props(AVFilterLink *outlink)
> +{
> + AVFilterContext *ctx = outlink->src;
> + ScaleD3D11Context *s = ctx->priv;
> + AVFilterLink *inlink = ctx->inputs[0];
> + FilterLink *inl = ff_filter_link(inlink);
> + FilterLink *outl = ff_filter_link(outlink);
> + int ret;
> +
> + ///< Clean up any previous resources
> + release_d3d11_resources(s);
> +
> + ///< Evaluate output dimensions
> + ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &s->width, &s->height);
> + if (ret < 0) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
> + return ret;
> + }
> +
> + outlink->w = s->width;
> + outlink->h = s->height;
> +
> + ///< Validate input hw_frames_ctx
> + if (!inl->hw_frames_ctx) {
> + av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on input link\n");
> + return AVERROR(EINVAL);
> + }
> +
> + ///< Propagate hw_frames_ctx to output
> + outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
> + if (!outl->hw_frames_ctx) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx to output\n");
> + return AVERROR(ENOMEM);
> + }
> +
> + ///< Initialize filter's hardware device context
> + if (!s->hw_device_ctx) {
> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
> + s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
> + if (!s->hw_device_ctx) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter hardware device context\n");
> + return AVERROR(ENOMEM);
> + }
> + }
> +
> + ///< Get D3D11 device and context (but don't initialize processor yet - done in filter_frame)
> + AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
> + AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx;
> +
> + s->device = d3d11_hwctx->device;
> + s->context = d3d11_hwctx->device_context;
> +
> + if (!s->device || !s->context) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device or context\n");
> + return AVERROR(EINVAL);
> + }
> +
> + ///< Create new hardware frames context for output
> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
> + s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
> + if (!s->hw_frames_ctx_out)
> + return AVERROR(ENOMEM);
> +
> + enum AVPixelFormat sw_format;
> + switch (s->output_format_opt) {
> + case OUTPUT_NV12:
> + sw_format = AV_PIX_FMT_NV12;
> + break;
> + case OUTPUT_P010:
> + sw_format = AV_PIX_FMT_P010;
> + break;
> + default:
> + return AVERROR(EINVAL);
> + }
> +
> + AVHWFramesContext *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data;
> + frames_ctx->format = AV_PIX_FMT_D3D11;
> + frames_ctx->sw_format = sw_format;
> + frames_ctx->width = s->width;
> + frames_ctx->height = s->height;
> + frames_ctx->initial_pool_size = 30; ///< Adjust pool size as needed
> +
> + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
> + frames_hwctx->MiscFlags = 0;
> + frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER;
> +
> + ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
> + if (ret < 0) {
> + av_buffer_unref(&s->hw_frames_ctx_out);
> + return ret;
> + }
> +
> + outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
> + if (!outl->hw_frames_ctx)
> + return AVERROR(ENOMEM);
> +
> + av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale config: %dx%d -> %dx%d\n",
> + inlink->w, inlink->h, outlink->w, outlink->h);
> + return 0;
> +}
> +
> +static av_cold void scale_d3d11_uninit(AVFilterContext *ctx) {
> + ScaleD3D11Context *s = ctx->priv;
> +
> + ///< Release D3D11 resources
> + release_d3d11_resources(s);
> +
> + ///< Free the hardware device context reference
> + av_buffer_unref(&s->hw_frames_ctx_out);
> + av_buffer_unref(&s->hw_device_ctx);
> +
> + ///< Free option strings
> + av_freep(&s->w_expr);
> + av_freep(&s->h_expr);
> +}
> +
> +static const AVFilterPad scale_d3d11_inputs[] = {
> + {
> + .name = "default",
> + .type = AVMEDIA_TYPE_VIDEO,
> + .filter_frame = scale_d3d11_filter_frame,
> + },
> +};
> +
> +static const AVFilterPad scale_d3d11_outputs[] = {
> + {
> + .name = "default",
> + .type = AVMEDIA_TYPE_VIDEO,
> + .config_props = scale_d3d11_config_props,
> + },
> +};
> +
> +#define OFFSET(x) offsetof(ScaleD3D11Context, x)
> +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
> +
> +static const AVOption scale_d3d11_options[] = {
> + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
> + { "height", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
> + { "output_fmt", "Output format", OFFSET(output_format_opt), AV_OPT_TYPE_INT, {.i64 = OUTPUT_NV12}, 0, OUTPUT_P010, FLAGS, "fmt" },
> + { "nv12", "NV12 format", 0, AV_OPT_TYPE_CONST, {.i64 = OUTPUT_NV12}, 0, 0, FLAGS, "fmt" },
> + { "p010", "P010 format", 0, AV_OPT_TYPE_CONST, {.i64 = OUTPUT_P010}, 0, 0, FLAGS, "fmt" },
> + { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(scale_d3d11);
> +
> +const FFFilter ff_vf_scale_d3d11 = {
> + .p.name = "scale_d3d11",
> + .p.description = NULL_IF_CONFIG_SMALL("Scale video using Direct3D11"),
> + .priv_size = sizeof(ScaleD3D11Context),
> + .p.priv_class = &scale_d3d11_class,
> + .init = scale_d3d11_init,
> + .uninit = scale_d3d11_uninit,
> + FILTER_INPUTS(scale_d3d11_inputs),
> + FILTER_OUTPUTS(scale_d3d11_outputs),
> + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
> + .p.flags = AVFILTER_FLAG_HWDEVICE,
> + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
> +};
> \ No newline at end of file
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> index 1a047ce57b..36694896e4 100644
> --- a/libavutil/hwcontext_d3d11va.c
> +++ b/libavutil/hwcontext_d3d11va.c
> @@ -82,6 +82,8 @@ typedef struct D3D11VAFramesContext {
>
> int nb_surfaces;
> int nb_surfaces_used;
> + int retries;
> + int max_retries;
>
> DXGI_FORMAT format;
>
> @@ -258,7 +260,9 @@ static AVBufferRef *d3d11va_pool_alloc(void *opaque, size_t size)
> ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
>
> if (s->nb_surfaces_used >= texDesc.ArraySize) {
> - av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
> + if (s->retries >= s->max_retries) {
> + av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
> + }
> return NULL;
> }
>
> @@ -339,20 +343,30 @@ static int d3d11va_frames_init(AVHWFramesContext *ctx)
> static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
> {
> AVD3D11FrameDescriptor *desc;
> + D3D11VAFramesContext *s = ctx->hwctx;
> + s->retries = 0;
> + s->max_retries = 50;
> +
> + while (s->retries < s->max_retries) {
> +
> + frame->buf[0] = av_buffer_pool_get(ctx->pool);
> + if (frame->buf[0]) {
> + desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
> +
> + frame->data[0] = (uint8_t *)desc->texture;
> + frame->data[1] = (uint8_t *)desc->index;
> + frame->format = AV_PIX_FMT_D3D11;
> + frame->width = ctx->width;
> + frame->height = ctx->height;
> +
> + return 0;
> + }
>
> - frame->buf[0] = av_buffer_pool_get(ctx->pool);
> - if (!frame->buf[0])
> - return AVERROR(ENOMEM);
> -
> - desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
> -
> - frame->data[0] = (uint8_t *)desc->texture;
> - frame->data[1] = (uint8_t *)desc->index;
> - frame->format = AV_PIX_FMT_D3D11;
> - frame->width = ctx->width;
> - frame->height = ctx->height;
> + av_usleep(1000);
> + s->retries++;
> + }
>
> - return 0;
> + return AVERROR(ENOMEM);
> }
>
> static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
2025-05-22 15:25 ` Timo Rothenpieler
@ 2025-05-22 17:57 ` Dash Santosh Sathyanarayanan
2025-05-22 18:42 ` softworkz .
0 siblings, 1 reply; 5+ messages in thread
From: Dash Santosh Sathyanarayanan @ 2025-05-22 17:57 UTC (permalink / raw)
To: ffmpeg-devel
On 22-05-2025 20:55, Timo Rothenpieler wrote:
> On 22/05/2025 15:20, Dash Santosh Sathyanarayanan wrote:
>> This commit introduces a new hardware-accelerated video filter,
>> scale_d3d11,
>> which performs scaling and format conversion using Direct3D 11. The
>> filter enables
>> efficient GPU-based scaling and pixel format conversion (p010 to
>> nv12), reducing
>> CPU overhead and latency in video pipelines.
>> ---
>> Changelog | 1 +
>> libavcodec/decode.c | 2 +-
>> libavcodec/dxva2.c | 3 +
>> libavfilter/Makefile | 1 +
>> libavfilter/allfilters.c | 1 +
>> libavfilter/vf_scale_d3d11.c | 480 ++++++++++++++++++++++++++++++++++
>> libavutil/hwcontext_d3d11va.c | 40 ++-
>> 7 files changed, 514 insertions(+), 14 deletions(-)
>> create mode 100644 libavfilter/vf_scale_d3d11.c
>>
>> diff --git a/Changelog b/Changelog
>> index 4217449438..68610a63d0 100644
>> --- a/Changelog
>> +++ b/Changelog
>> @@ -18,6 +18,7 @@ version <next>:
>> - APV encoding support through a libopenapv wrapper
>> - VVC decoder supports all content of SCC (Screen Content Coding):
>> IBC (Inter Block Copy), Palette Mode and ACT (Adaptive Color
>> Transform
>> +- vf_scale_d3d11 filter
>
> Bit of a nit, this could at last say "Added".
Oops, sorry. My bad.
>
>> version 7.1:
>> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
>> index c2b2dd6e3b..a796ae7930 100644
>> --- a/libavcodec/decode.c
>> +++ b/libavcodec/decode.c
>> @@ -1079,7 +1079,7 @@ int ff_decode_get_hw_frames_ctx(AVCodecContext
>> *avctx,
>> if (frames_ctx->initial_pool_size) {
>> // We guarantee 4 base work surfaces. The function above
>> guarantees 1
>> // (the absolute minimum), so add the missing count.
>> - frames_ctx->initial_pool_size += 3;
>> + frames_ctx->initial_pool_size += 33;
>
> This seems a bit extreme, and can potentially drastically increase
> VRAM usage of anything using d3d11va.
In full hardware pipeline, when all surfaces are in use, we hit pool
exhaustion and 'static pool size exceeded' error occurs. Hence the
change. The increase in memory footprint was about ~100mb with this change.
>
>> }
>> ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
>> diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
>> index 22ecd5acaf..37dab6cd68 100644
>> --- a/libavcodec/dxva2.c
>> +++ b/libavcodec/dxva2.c
>> @@ -647,6 +647,9 @@ int ff_dxva2_common_frame_params(AVCodecContext
>> *avctx,
>> AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
>> frames_hwctx->BindFlags |= D3D11_BIND_DECODER;
>> + if (frames_ctx->sw_format == AV_PIX_FMT_NV12) {
>> + frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER;
>> + }
>
> This change also seems a bit random here. Using NV12 does not
> automatically mean you'll encode with it.
The encoder requires D3D11_BIND_VIDEO_ENCODER to be set in the input
surface, when sending the D3D11 surface directly to MF encoder.
Currently MF encoder supports only 8bit (NV12). Hence the change. If the
input is 10bit (P010), scale_d3d11 can be configured to output 8bit NV12
frames.
>
>
> Did not look at the rest yet.
>> + return AVERROR_EXTERNAL;
>> + }
>> +
>> + ///< Set up output frame
>> + ret = av_frame_copy_props(out, in);
>> + if (ret < 0) {
>> + av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
>> + videoContext->lpVtbl->Release(videoContext);
>> + inputView->lpVtbl->Release(inputView);
>> + av_frame_free(&in);
>> + av_frame_free(&out);
>> + return ret;
>> + }
>> +
>> + out->data[0] = (uint8_t *)output_texture;
>> + out->data[1] = (uint8_t *)(intptr_t)0;
>> + out->width = s->width;
>> + out->height = s->height;
>> + out->format = AV_PIX_FMT_D3D11;
>> +
>> + ///< Clean up resources
>> + inputView->lpVtbl->Release(inputView);
>> + videoContext->lpVtbl->Release(videoContext);
>> + if (s->outputView) {
>> + s->outputView->lpVtbl->Release(s->outputView);
>> + s->outputView = NULL;
>> + }
>> + av_frame_free(&in);
>> +
>> + ///< Forward the frame
>> + return ff_filter_frame(outlink, out);
>> +}
>> +
>> +static int scale_d3d11_config_props(AVFilterLink *outlink)
>> +{
>> + AVFilterContext *ctx = outlink->src;
>> + ScaleD3D11Context *s = ctx->priv;
>> + AVFilterLink *inlink = ctx->inputs[0];
>> + FilterLink *inl = ff_filter_link(inlink);
>> + FilterLink *outl = ff_filter_link(outlink);
>> + int ret;
>> +
>> + ///< Clean up any previous resources
>> + release_d3d11_resources(s);
>> +
>> + ///< Evaluate output dimensions
>> + ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink,
>> outlink, &s->width, &s->height);
>> + if (ret < 0) {
>> + av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
>> + return ret;
>> + }
>> +
>> + outlink->w = s->width;
>> + outlink->h = s->height;
>> +
>> + ///< Validate input hw_frames_ctx
>> + if (!inl->hw_frames_ctx) {
>> + av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on
>> input link\n");
>> + return AVERROR(EINVAL);
>> + }
>> +
>> + ///< Propagate hw_frames_ctx to output
>> + outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
>> + if (!outl->hw_frames_ctx) {
>> + av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx
>> to output\n");
>> + return AVERROR(ENOMEM);
>> + }
>> +
>> + ///< Initialize filter's hardware device context
>> + if (!s->hw_device_ctx) {
>> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
>> *)inl->hw_frames_ctx->data;
>> + s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
>> + if (!s->hw_device_ctx) {
>> + av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter
>> hardware device context\n");
>> + return AVERROR(ENOMEM);
>> + }
>> + }
>> +
>> + ///< Get D3D11 device and context (but don't initialize
>> processor yet - done in filter_frame)
>> + AVHWDeviceContext *hwctx = (AVHWDeviceContext
>> *)s->hw_device_ctx->data;
>> + AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext
>> *)hwctx->hwctx;
>> +
>> + s->device = d3d11_hwctx->device;
>> + s->context = d3d11_hwctx->device_context;
>> +
>> + if (!s->device || !s->context) {
>> + av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device
>> or context\n");
>> + return AVERROR(EINVAL);
>> + }
>> +
>> + ///< Create new hardware frames context for output
>> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
>> *)inl->hw_frames_ctx->data;
>> + s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
>> + if (!s->hw_frames_ctx_out)
>> + return AVERROR(ENOMEM);
>> +
>> + enum AVPixelFormat sw_format;
>> + switch (s->output_format_opt) {
>> + case OUTPUT_NV12:
>> + sw_format = AV_PIX_FMT_NV12;
>> + break;
>> + case OUTPUT_P010:
>> + sw_format = AV_PIX_FMT_P010;
>> + break;
>> + default:
>> + return AVERROR(EINVAL);
>> + }
>> +
>> + AVHWFramesContext *frames_ctx = (AVHWFramesContext
>> *)s->hw_frames_ctx_out->data;
>> + frames_ctx->format = AV_PIX_FMT_D3D11;
>> + frames_ctx->sw_format = sw_format;
>> + frames_ctx->width = s->width;
>> + frames_ctx->height = s->height;
>> + frames_ctx->initial_pool_size = 30; ///< Adjust pool size as needed
>> +
>> + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
>> + frames_hwctx->MiscFlags = 0;
>> + frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET |
>> D3D11_BIND_VIDEO_ENCODER;
>> +
>> + ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
>> + if (ret < 0) {
>> + av_buffer_unref(&s->hw_frames_ctx_out);
>> + return ret;
>> + }
>> +
>> + outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
>> + if (!outl->hw_frames_ctx)
>> + return AVERROR(ENOMEM);
>> +
>> + av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale config: %dx%d -> %dx%d\n",
>> + inlink->w, inlink->h, outlink->w, outlink->h);
>> + return 0;
>> +}
>> +
>> +static av_cold void scale_d3d11_uninit(AVFilterContext *ctx) {
>> + ScaleD3D11Context *s = ctx->priv;
>> +
>> + ///< Release D3D11 resources
>> + release_d3d11_resources(s);
>> +
>> + ///< Free the hardware device context reference
>> + av_buffer_unref(&s->hw_frames_ctx_out);
>> + av_buffer_unref(&s->hw_device_ctx);
>> +
>> + ///< Free option strings
>> + av_freep(&s->w_expr);
>> + av_freep(&s->h_expr);
>> +}
>> +
>> +static const AVFilterPad scale_d3d11_inputs[] = {
>> + {
>> + .name = "default",
>> + .type = AVMEDIA_TYPE_VIDEO,
>> + .filter_frame = scale_d3d11_filter_frame,
>> + },
>> +};
>> +
>> +static const AVFilterPad scale_d3d11_outputs[] = {
>> + {
>> + .name = "default",
>> + .type = AVMEDIA_TYPE_VIDEO,
>> + .config_props = scale_d3d11_config_props,
>> + },
>> +};
>> +
>> +#define OFFSET(x) offsetof(ScaleD3D11Context, x)
>> +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
>> +
>> +static const AVOption scale_d3d11_options[] = {
>> + { "width", "Output video width", OFFSET(w_expr),
>> AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
>> + { "height", "Output video height", OFFSET(h_expr),
>> AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
>> + { "output_fmt", "Output format", OFFSET(output_format_opt),
>> AV_OPT_TYPE_INT, {.i64 = OUTPUT_NV12}, 0, OUTPUT_P010, FLAGS, "fmt" },
>> + { "nv12", "NV12 format", 0, AV_OPT_TYPE_CONST, {.i64 =
>> OUTPUT_NV12}, 0, 0, FLAGS, "fmt" },
>> + { "p010", "P010 format", 0, AV_OPT_TYPE_CONST, {.i64 =
>> OUTPUT_P010}, 0, 0, FLAGS, "fmt" },
>> + { NULL }
>> +};
>> +
>> +AVFILTER_DEFINE_CLASS(scale_d3d11);
>> +
>> +const FFFilter ff_vf_scale_d3d11 = {
>> + .p.name = "scale_d3d11",
>> + .p.description = NULL_IF_CONFIG_SMALL("Scale video using
>> Direct3D11"),
>> + .priv_size = sizeof(ScaleD3D11Context),
>> + .p.priv_class = &scale_d3d11_class,
>> + .init = scale_d3d11_init,
>> + .uninit = scale_d3d11_uninit,
>> + FILTER_INPUTS(scale_d3d11_inputs),
>> + FILTER_OUTPUTS(scale_d3d11_outputs),
>> + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
>> + .p.flags = AVFILTER_FLAG_HWDEVICE,
>> + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
>> +};
>> \ No newline at end of file
>> diff --git a/libavutil/hwcontext_d3d11va.c
>> b/libavutil/hwcontext_d3d11va.c
>> index 1a047ce57b..36694896e4 100644
>> --- a/libavutil/hwcontext_d3d11va.c
>> +++ b/libavutil/hwcontext_d3d11va.c
>> @@ -82,6 +82,8 @@ typedef struct D3D11VAFramesContext {
>> int nb_surfaces;
>> int nb_surfaces_used;
>> + int retries;
>> + int max_retries;
>> DXGI_FORMAT format;
>> @@ -258,7 +260,9 @@ static AVBufferRef *d3d11va_pool_alloc(void
>> *opaque, size_t size)
>> ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
>> if (s->nb_surfaces_used >= texDesc.ArraySize) {
>> - av_log(ctx, AV_LOG_ERROR, "Static surface pool size
>> exceeded.\n");
>> + if (s->retries >= s->max_retries) {
>> + av_log(ctx, AV_LOG_ERROR, "Static surface pool size
>> exceeded.\n");
>> + }
>> return NULL;
>> }
>> @@ -339,20 +343,30 @@ static int
>> d3d11va_frames_init(AVHWFramesContext *ctx)
>> static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
>> {
>> AVD3D11FrameDescriptor *desc;
>> + D3D11VAFramesContext *s = ctx->hwctx;
>> + s->retries = 0;
>> + s->max_retries = 50;
>> +
>> + while (s->retries < s->max_retries) {
>> +
>> + frame->buf[0] = av_buffer_pool_get(ctx->pool);
>> + if (frame->buf[0]) {
>> + desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
>> +
>> + frame->data[0] = (uint8_t *)desc->texture;
>> + frame->data[1] = (uint8_t *)desc->index;
>> + frame->format = AV_PIX_FMT_D3D11;
>> + frame->width = ctx->width;
>> + frame->height = ctx->height;
>> +
>> + return 0;
>> + }
>> - frame->buf[0] = av_buffer_pool_get(ctx->pool);
>> - if (!frame->buf[0])
>> - return AVERROR(ENOMEM);
>> -
>> - desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
>> -
>> - frame->data[0] = (uint8_t *)desc->texture;
>> - frame->data[1] = (uint8_t *)desc->index;
>> - frame->format = AV_PIX_FMT_D3D11;
>> - frame->width = ctx->width;
>> - frame->height = ctx->height;
>> + av_usleep(1000);
>> + s->retries++;
>> + }
>> - return 0;
>> + return AVERROR(ENOMEM);
>> }
>> static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
>>
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
2025-05-22 17:57 ` Dash Santosh Sathyanarayanan
@ 2025-05-22 18:42 ` softworkz .
2025-05-30 8:01 ` Dash Santosh Sathyanarayanan
0 siblings, 1 reply; 5+ messages in thread
From: softworkz . @ 2025-05-22 18:42 UTC (permalink / raw)
To: FFmpeg development discussions and patches
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dash Santosh
> Sathyanarayanan
> Sent: Donnerstag, 22. Mai 2025 19:58
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
>
> On 22-05-2025 20:55, Timo Rothenpieler wrote:
> > On 22/05/2025 15:20, Dash Santosh Sathyanarayanan wrote:
> >> This commit introduces a new hardware-accelerated video filter,
> >> scale_d3d11,
> >> which performs scaling and format conversion using Direct3D 11. The
> >> filter enables
> >> efficient GPU-based scaling and pixel format conversion (p010 to
> >> nv12), reducing
> >> CPU overhead and latency in video pipelines.
> >> ---
> >> Changelog | 1 +
> >> libavcodec/decode.c | 2 +-
> >> libavcodec/dxva2.c | 3 +
> >> libavfilter/Makefile | 1 +
> >> libavfilter/allfilters.c | 1 +
> >> libavfilter/vf_scale_d3d11.c | 480 ++++++++++++++++++++++++++++++++++
> >> libavutil/hwcontext_d3d11va.c | 40 ++-
> >> 7 files changed, 514 insertions(+), 14 deletions(-)
> >> create mode 100644 libavfilter/vf_scale_d3d11.c
> >>
> >> diff --git a/Changelog b/Changelog
> >> index 4217449438..68610a63d0 100644
> >> --- a/Changelog
> >> +++ b/Changelog
> >> @@ -18,6 +18,7 @@ version <next>:
> >> - APV encoding support through a libopenapv wrapper
> >> - VVC decoder supports all content of SCC (Screen Content Coding):
> >> IBC (Inter Block Copy), Palette Mode and ACT (Adaptive Color
> >> Transform
> >> +- vf_scale_d3d11 filter
> >
> > Bit of a nit, this could at last say "Added".
> Oops, sorry. My bad.
> >
> >> version 7.1:
> >> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> >> index c2b2dd6e3b..a796ae7930 100644
> >> --- a/libavcodec/decode.c
> >> +++ b/libavcodec/decode.c
> >> @@ -1079,7 +1079,7 @@ int ff_decode_get_hw_frames_ctx(AVCodecContext
> >> *avctx,
> >> if (frames_ctx->initial_pool_size) {
> >> // We guarantee 4 base work surfaces. The function above
> >> guarantees 1
> >> // (the absolute minimum), so add the missing count.
> >> - frames_ctx->initial_pool_size += 3;
> >> + frames_ctx->initial_pool_size += 33;
> >
> > This seems a bit extreme, and can potentially drastically increase
> > VRAM usage of anything using d3d11va.
> In full hardware pipeline, when all surfaces are in use, we hit pool
> exhaustion and 'static pool size exceeded' error occurs. Hence the
> change. The increase in memory footprint was about ~100mb with this change.
Hi,
this is not the right place for this change as it affects all hw accelerations.
Also, it sets it unconditionally, even when no filter is in play.
Since there is no direct relation between the decoder and the filter,
it's hard for the decoder to "know" whether its output will be connected
to a filter. The current way to handle this is to specify the
extra_hw_frames parameter on the command line in cases where more frames
are needed.
Note that there's a extra_hw_frames decoder parameter and a extra_hw_frames
filter parameter. Here's an example where it is used:
https://trac.ffmpeg.org/wiki/Hardware/AMF
> >> ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
> >> diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
> >> index 22ecd5acaf..37dab6cd68 100644
> >> --- a/libavcodec/dxva2.c
> >> +++ b/libavcodec/dxva2.c
> >> @@ -647,6 +647,9 @@ int ff_dxva2_common_frame_params(AVCodecContext
> >> *avctx,
> >> AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
> >> frames_hwctx->BindFlags |= D3D11_BIND_DECODER;
> >> + if (frames_ctx->sw_format == AV_PIX_FMT_NV12) {
> >> + frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER;
> >> + }
> >
> > This change also seems a bit random here. Using NV12 does not
> > automatically mean you'll encode with it.
> The encoder requires D3D11_BIND_VIDEO_ENCODER to be set in the input
> surface, when sending the D3D11 surface directly to MF encoder.
> Currently MF encoder supports only 8bit (NV12). Hence the change. If the
> input is 10bit (P010), scale_d3d11 can be configured to output 8bit NV12
> frames.
The filter should be designed universally rather than expecting to be connected
to something specific at its output. Whether or not the bind_encoder bindflag
should be set, needs to be determined during negotiation of the output
connection.
In case you wonder, what else could come after this filter:
- another instance of this filter
- a hwmap filter to derive to a QSV context (followed by QSV filters or a QSV encoder)
- a hwmap filter to other contexts (maybe AMF)
- the Nvidia NVENC encoders are said to be able to take D3D11 frames as input directly
(without hwmap), I've never tried that, but if it works, it might also work to
connect them to this filter (given that the D3D11 context is an Nvidia GPU)
I don't mean to say that you should get all this working - just illustrate
the scope of possibilities.
> >
> >
> > Did not look at the rest yet.
> >> + return AVERROR_EXTERNAL;
> >> + }
> >> +
> >> + ///< Set up output frame
> >> + ret = av_frame_copy_props(out, in);
> >> + if (ret < 0) {
> >> + av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
> >> + videoContext->lpVtbl->Release(videoContext);
> >> + inputView->lpVtbl->Release(inputView);
> >> + av_frame_free(&in);
> >> + av_frame_free(&out);
> >> + return ret;
> >> + }
> >> +
> >> + out->data[0] = (uint8_t *)output_texture;
> >> + out->data[1] = (uint8_t *)(intptr_t)0;
> >> + out->width = s->width;
> >> + out->height = s->height;
> >> + out->format = AV_PIX_FMT_D3D11;
> >> +
> >> + ///< Clean up resources
> >> + inputView->lpVtbl->Release(inputView);
> >> + videoContext->lpVtbl->Release(videoContext);
> >> + if (s->outputView) {
> >> + s->outputView->lpVtbl->Release(s->outputView);
> >> + s->outputView = NULL;
> >> + }
> >> + av_frame_free(&in);
> >> +
> >> + ///< Forward the frame
> >> + return ff_filter_frame(outlink, out);
> >> +}
> >> +
> >> +static int scale_d3d11_config_props(AVFilterLink *outlink)
> >> +{
> >> + AVFilterContext *ctx = outlink->src;
> >> + ScaleD3D11Context *s = ctx->priv;
> >> + AVFilterLink *inlink = ctx->inputs[0];
> >> + FilterLink *inl = ff_filter_link(inlink);
> >> + FilterLink *outl = ff_filter_link(outlink);
> >> + int ret;
> >> +
> >> + ///< Clean up any previous resources
> >> + release_d3d11_resources(s);
> >> +
> >> + ///< Evaluate output dimensions
> >> + ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink,
> >> outlink, &s->width, &s->height);
> >> + if (ret < 0) {
> >> + av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
> >> + return ret;
> >> + }
> >> +
> >> + outlink->w = s->width;
> >> + outlink->h = s->height;
> >> +
> >> + ///< Validate input hw_frames_ctx
> >> + if (!inl->hw_frames_ctx) {
> >> + av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on
> >> input link\n");
> >> + return AVERROR(EINVAL);
> >> + }
> >> +
> >> + ///< Propagate hw_frames_ctx to output
> >> + outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
> >> + if (!outl->hw_frames_ctx) {
> >> + av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx
> >> to output\n");
> >> + return AVERROR(ENOMEM);
> >> + }
> >> +
> >> + ///< Initialize filter's hardware device context
> >> + if (!s->hw_device_ctx) {
> >> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
> >> *)inl->hw_frames_ctx->data;
> >> + s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
> >> + if (!s->hw_device_ctx) {
> >> + av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter
> >> hardware device context\n");
> >> + return AVERROR(ENOMEM);
> >> + }
> >> + }
> >> +
> >> + ///< Get D3D11 device and context (but don't initialize
> >> processor yet - done in filter_frame)
> >> + AVHWDeviceContext *hwctx = (AVHWDeviceContext
> >> *)s->hw_device_ctx->data;
> >> + AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext
> >> *)hwctx->hwctx;
> >> +
> >> + s->device = d3d11_hwctx->device;
> >> + s->context = d3d11_hwctx->device_context;
> >> +
> >> + if (!s->device || !s->context) {
> >> + av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device
> >> or context\n");
> >> + return AVERROR(EINVAL);
> >> + }
> >> +
> >> + ///< Create new hardware frames context for output
> >> + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
> >> *)inl->hw_frames_ctx->data;
> >> + s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
> >> + if (!s->hw_frames_ctx_out)
> >> + return AVERROR(ENOMEM);
> >> +
> >> + enum AVPixelFormat sw_format;
> >> + switch (s->output_format_opt) {
> >> + case OUTPUT_NV12:
> >> + sw_format = AV_PIX_FMT_NV12;
> >> + break;
> >> + case OUTPUT_P010:
> >> + sw_format = AV_PIX_FMT_P010;
> >> + break;
> >> + default:
> >> + return AVERROR(EINVAL);
> >> + }
> >> +
> >> + AVHWFramesContext *frames_ctx = (AVHWFramesContext
> >> *)s->hw_frames_ctx_out->data;
> >> + frames_ctx->format = AV_PIX_FMT_D3D11;
> >> + frames_ctx->sw_format = sw_format;
> >> + frames_ctx->width = s->width;
> >> + frames_ctx->height = s->height;
> >> + frames_ctx->initial_pool_size = 30; ///< Adjust pool size as needed
This should have a lower default and use the extra_hw_frames option that all
filters have to add to it.
> >> +
> >> + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
> >> + frames_hwctx->MiscFlags = 0;
> >> + frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET |
> >> D3D11_BIND_VIDEO_ENCODER;
> >> +
> >> + ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
> >> + if (ret < 0) {
> >> + av_buffer_unref(&s->hw_frames_ctx_out);
> >> + return ret;
> >> + }
> >> +
> >> + outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
> >> + if (!outl->hw_frames_ctx)
> >> + return AVERROR(ENOMEM);
> >> +
> >> + av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale config: %dx%d -> %dx%d\n",
> >> + inlink->w, inlink->h, outlink->w, outlink->h);
> >> + return 0;
> >> +}
> >> +
> >> +static av_cold void scale_d3d11_uninit(AVFilterContext *ctx) {
> >> + ScaleD3D11Context *s = ctx->priv;
> >> +
> >> + ///< Release D3D11 resources
> >> + release_d3d11_resources(s);
> >> +
> >> + ///< Free the hardware device context reference
> >> + av_buffer_unref(&s->hw_frames_ctx_out);
> >> + av_buffer_unref(&s->hw_device_ctx);
> >> +
> >> + ///< Free option strings
> >> + av_freep(&s->w_expr);
> >> + av_freep(&s->h_expr);
> >> +}
> >> +
> >> +static const AVFilterPad scale_d3d11_inputs[] = {
> >> + {
> >> + .name = "default",
> >> + .type = AVMEDIA_TYPE_VIDEO,
> >> + .filter_frame = scale_d3d11_filter_frame,
> >> + },
> >> +};
> >> +
> >> +static const AVFilterPad scale_d3d11_outputs[] = {
> >> + {
> >> + .name = "default",
> >> + .type = AVMEDIA_TYPE_VIDEO,
> >> + .config_props = scale_d3d11_config_props,
> >> + },
> >> +};
> >> +
> >> +#define OFFSET(x) offsetof(ScaleD3D11Context, x)
> >> +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
> >> +
> >> +static const AVOption scale_d3d11_options[] = {
> >> + { "width", "Output video width", OFFSET(w_expr),
> >> AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
> >> + { "height", "Output video height", OFFSET(h_expr),
> >> AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
> >> + { "output_fmt", "Output format", OFFSET(output_format_opt),
> >> AV_OPT_TYPE_INT, {.i64 = OUTPUT_NV12}, 0, OUTPUT_P010, FLAGS, "fmt" },
> >> + { "nv12", "NV12 format", 0, AV_OPT_TYPE_CONST, {.i64 =
> >> OUTPUT_NV12}, 0, 0, FLAGS, "fmt" },
> >> + { "p010", "P010 format", 0, AV_OPT_TYPE_CONST, {.i64 =
> >> OUTPUT_P010}, 0, 0, FLAGS, "fmt" },
> >> + { NULL }
There's a specific option type for that: AV_OPT_TYPE_PIXEL_FMT
> >> +};
> >> +
> >> +AVFILTER_DEFINE_CLASS(scale_d3d11);
> >> +
> >> +const FFFilter ff_vf_scale_d3d11 = {
> >> + .p.name = "scale_d3d11",
> >> + .p.description = NULL_IF_CONFIG_SMALL("Scale video using
> >> Direct3D11"),
> >> + .priv_size = sizeof(ScaleD3D11Context),
> >> + .p.priv_class = &scale_d3d11_class,
> >> + .init = scale_d3d11_init,
> >> + .uninit = scale_d3d11_uninit,
> >> + FILTER_INPUTS(scale_d3d11_inputs),
> >> + FILTER_OUTPUTS(scale_d3d11_outputs),
> >> + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
> >> + .p.flags = AVFILTER_FLAG_HWDEVICE,
> >> + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
> >> +};
> >> \ No newline at end of file
> >> diff --git a/libavutil/hwcontext_d3d11va.c
> >> b/libavutil/hwcontext_d3d11va.c
> >> index 1a047ce57b..36694896e4 100644
> >> --- a/libavutil/hwcontext_d3d11va.c
> >> +++ b/libavutil/hwcontext_d3d11va.c
> >> @@ -82,6 +82,8 @@ typedef struct D3D11VAFramesContext {
> >> int nb_surfaces;
> >> int nb_surfaces_used;
> >> + int retries;
> >> + int max_retries;
> >> DXGI_FORMAT format;
> >> @@ -258,7 +260,9 @@ static AVBufferRef *d3d11va_pool_alloc(void
> >> *opaque, size_t size)
> >> ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
> >> if (s->nb_surfaces_used >= texDesc.ArraySize) {
> >> - av_log(ctx, AV_LOG_ERROR, "Static surface pool size
> >> exceeded.\n");
> >> + if (s->retries >= s->max_retries) {
> >> + av_log(ctx, AV_LOG_ERROR, "Static surface pool size
> >> exceeded.\n");
> >> + }
> >> return NULL;
> >> }
> >> @@ -339,20 +343,30 @@ static int
> >> d3d11va_frames_init(AVHWFramesContext *ctx)
> >> static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
> >> {
> >> AVD3D11FrameDescriptor *desc;
> >> + D3D11VAFramesContext *s = ctx->hwctx;
> >> + s->retries = 0;
> >> + s->max_retries = 50;
> >> +
> >> + while (s->retries < s->max_retries) {
> >> +
> >> + frame->buf[0] = av_buffer_pool_get(ctx->pool);
> >> + if (frame->buf[0]) {
> >> + desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
> >> +
> >> + frame->data[0] = (uint8_t *)desc->texture;
> >> + frame->data[1] = (uint8_t *)desc->index;
> >> + frame->format = AV_PIX_FMT_D3D11;
> >> + frame->width = ctx->width;
> >> + frame->height = ctx->height;
> >> +
> >> + return 0;
> >> + }
> >> - frame->buf[0] = av_buffer_pool_get(ctx->pool);
> >> - if (!frame->buf[0])
> >> - return AVERROR(ENOMEM);
> >> -
> >> - desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
> >> -
> >> - frame->data[0] = (uint8_t *)desc->texture;
> >> - frame->data[1] = (uint8_t *)desc->index;
> >> - frame->format = AV_PIX_FMT_D3D11;
> >> - frame->width = ctx->width;
> >> - frame->height = ctx->height;
> >> + av_usleep(1000);
> >> + s->retries++;
> >> + }
> >> - return 0;
> >> + return AVERROR(ENOMEM);
> >> }
> >> static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
I'm afraid, but this loop is not right. Please take a look at how other
filters are handling the case when no hardware frame is available.
Thanks
sw
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
2025-05-22 18:42 ` softworkz .
@ 2025-05-30 8:01 ` Dash Santosh Sathyanarayanan
0 siblings, 0 replies; 5+ messages in thread
From: Dash Santosh Sathyanarayanan @ 2025-05-30 8:01 UTC (permalink / raw)
To: ffmpeg-devel
Hi,
Thanks for your review and inputs. I understand.
Instead of making the changes here, I'm trying to create a hw_frames_ctx with required config and pass it while opening the decoder, but I'm facing exceptions at av_buffer_unref(&dst->hw_frames_ctx) in update_context_from_thread. Call stack attached below. Could you help in understanding this error please? Appreciate your time.
I'll share the updated patch for scale_d3d11 filter after resolving these errors.
Call stack:
Unhandled exception at 0x00007FFAE1644FCC (ntdll.dll) in HandBrakeCLI.exe: Unknown __fastfail() status code: 0x0000000000000023.
[Inline Frame] buffer_replace(AVBufferRef * * dst, AVBufferRef * * src) Line 121 (ffmpeg\libavutil\buffer.c:121)
av_buffer_unref(AVBufferRef * * buf) Line 144 (ffmpeg\libavutil\buffer.c:144)
update_context_from_thread(AVCodecContext * dst, const AVCodecContext * src, int for_user) Line 399 (ffmpeg\libavcodec\pthread_frame.c:399)
[Inline Frame] submit_packet(PerThreadContext * p, AVCodecContext * user_avctx, AVPacket * in_pkt) Line 540 (ffmpeg\libavcodec\pthread_frame.c:540)
ff_thread_receive_frame(AVCodecContext * avctx, AVFrame * frame) Line 585 (ffmpeg\libavcodec\pthread_frame.c:585)
decode_receive_frame_internal(AVCodecContext * avctx, AVFrame * frame) Line 663 (ffmpeg\libavcodec\decode.c:663)
avcodec_send_packet(AVCodecContext * avctx, const AVPacket * avpkt) Line 753 (ffmpeg\libavcodec\decode.c:753)
decodeFrame(hb_work_private_s * pv, packet_info_t * packet_info) Line 1768 (HandBrake\libhb\decavcodec.c:1768)
decodePacket(hb_work_object_s * w) Line 2184 (HandBrake\libhb\decavcodec.c:2184)
decavcodecvWork(hb_work_object_s * w, hb_buffer_s * * buf_in, hb_buffer_s * * buf_out) Line 2353 (HandBrake\libhb\decavcodec.c:2353)
hb_work_loop(void * _w) Line 2350 (HandBrake\libhb\work.c:2350)
On 23-05-2025 00:12, softworkz . wrote:
-----Original Message-----
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org><mailto:ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dash Santosh
Sathyanarayanan
Sent: Donnerstag, 22. Mai 2025 19:58
To: ffmpeg-devel@ffmpeg.org<mailto:ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter
On 22-05-2025 20:55, Timo Rothenpieler wrote:
On 22/05/2025 15:20, Dash Santosh Sathyanarayanan wrote:
This commit introduces a new hardware-accelerated video filter,
scale_d3d11,
which performs scaling and format conversion using Direct3D 11. The
filter enables
efficient GPU-based scaling and pixel format conversion (p010 to
nv12), reducing
CPU overhead and latency in video pipelines.
---
Changelog | 1 +
libavcodec/decode.c | 2 +-
libavcodec/dxva2.c | 3 +
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_scale_d3d11.c | 480 ++++++++++++++++++++++++++++++++++
libavutil/hwcontext_d3d11va.c | 40 ++-
7 files changed, 514 insertions(+), 14 deletions(-)
create mode 100644 libavfilter/vf_scale_d3d11.c
diff --git a/Changelog b/Changelog
index 4217449438..68610a63d0 100644
--- a/Changelog
+++ b/Changelog
@@ -18,6 +18,7 @@ version <next>:
- APV encoding support through a libopenapv wrapper
- VVC decoder supports all content of SCC (Screen Content Coding):
IBC (Inter Block Copy), Palette Mode and ACT (Adaptive Color
Transform
+- vf_scale_d3d11 filter
Bit of a nit, this could at last say "Added".
Oops, sorry. My bad.
version 7.1:
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index c2b2dd6e3b..a796ae7930 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1079,7 +1079,7 @@ int ff_decode_get_hw_frames_ctx(AVCodecContext
*avctx,
if (frames_ctx->initial_pool_size) {
// We guarantee 4 base work surfaces. The function above
guarantees 1
// (the absolute minimum), so add the missing count.
- frames_ctx->initial_pool_size += 3;
+ frames_ctx->initial_pool_size += 33;
This seems a bit extreme, and can potentially drastically increase
VRAM usage of anything using d3d11va.
In full hardware pipeline, when all surfaces are in use, we hit pool
exhaustion and 'static pool size exceeded' error occurs. Hence the
change. The increase in memory footprint was about ~100mb with this change.
Hi,
this is not the right place for this change as it affects all hw accelerations.
Also, it sets it unconditionally, even when no filter is in play.
Since there is no direct relation between the decoder and the filter,
it's hard for the decoder to "know" whether its output will be connected
to a filter. The current way to handle this is to specify the
extra_hw_frames parameter on the command line in cases where more frames
are needed.
Note that there's a extra_hw_frames decoder parameter and a extra_hw_frames
filter parameter. Here's an example where it is used:
https://trac.ffmpeg.org/wiki/Hardware/AMF
ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
index 22ecd5acaf..37dab6cd68 100644
--- a/libavcodec/dxva2.c
+++ b/libavcodec/dxva2.c
@@ -647,6 +647,9 @@ int ff_dxva2_common_frame_params(AVCodecContext
*avctx,
AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
frames_hwctx->BindFlags |= D3D11_BIND_DECODER;
+ if (frames_ctx->sw_format == AV_PIX_FMT_NV12) {
+ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER;
+ }
This change also seems a bit random here. Using NV12 does not
automatically mean you'll encode with it.
The encoder requires D3D11_BIND_VIDEO_ENCODER to be set in the input
surface, when sending the D3D11 surface directly to MF encoder.
Currently MF encoder supports only 8bit (NV12). Hence the change. If the
input is 10bit (P010), scale_d3d11 can be configured to output 8bit NV12
frames.
The filter should be designed universally rather than expecting to be connected
to something specific at its output. Whether or not the bind_encoder bindflag
should be set, needs to be determined during negotiation of the output
connection.
In case you wonder, what else could come after this filter:
- another instance of this filter
- a hwmap filter to derive to a QSV context (followed by QSV filters or a QSV encoder)
- a hwmap filter to other contexts (maybe AMF)
- the Nvidia NVENC encoders are said to be able to take D3D11 frames as input directly
(without hwmap), I've never tried that, but if it works, it might also work to
connect them to this filter (given that the D3D11 context is an Nvidia GPU)
I don't mean to say that you should get all this working - just illustrate
the scope of possibilities.
Did not look at the rest yet.
+ return AVERROR_EXTERNAL;
+ }
+
+ ///< Set up output frame
+ ret = av_frame_copy_props(out, in);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
+ videoContext->lpVtbl->Release(videoContext);
+ inputView->lpVtbl->Release(inputView);
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return ret;
+ }
+
+ out->data[0] = (uint8_t *)output_texture;
+ out->data[1] = (uint8_t *)(intptr_t)0;
+ out->width = s->width;
+ out->height = s->height;
+ out->format = AV_PIX_FMT_D3D11;
+
+ ///< Clean up resources
+ inputView->lpVtbl->Release(inputView);
+ videoContext->lpVtbl->Release(videoContext);
+ if (s->outputView) {
+ s->outputView->lpVtbl->Release(s->outputView);
+ s->outputView = NULL;
+ }
+ av_frame_free(&in);
+
+ ///< Forward the frame
+ return ff_filter_frame(outlink, out);
+}
+
+static int scale_d3d11_config_props(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ ScaleD3D11Context *s = ctx->priv;
+ AVFilterLink *inlink = ctx->inputs[0];
+ FilterLink *inl = ff_filter_link(inlink);
+ FilterLink *outl = ff_filter_link(outlink);
+ int ret;
+
+ ///< Clean up any previous resources
+ release_d3d11_resources(s);
+
+ ///< Evaluate output dimensions
+ ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink,
outlink, &s->width, &s->height);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
+ return ret;
+ }
+
+ outlink->w = s->width;
+ outlink->h = s->height;
+
+ ///< Validate input hw_frames_ctx
+ if (!inl->hw_frames_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on
input link\n");
+ return AVERROR(EINVAL);
+ }
+
+ ///< Propagate hw_frames_ctx to output
+ outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
+ if (!outl->hw_frames_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx
to output\n");
+ return AVERROR(ENOMEM);
+ }
+
+ ///< Initialize filter's hardware device context
+ if (!s->hw_device_ctx) {
+ AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
*)inl->hw_frames_ctx->data;
+ s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
+ if (!s->hw_device_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter
hardware device context\n");
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ ///< Get D3D11 device and context (but don't initialize
processor yet - done in filter_frame)
+ AVHWDeviceContext *hwctx = (AVHWDeviceContext
*)s->hw_device_ctx->data;
+ AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext
*)hwctx->hwctx;
+
+ s->device = d3d11_hwctx->device;
+ s->context = d3d11_hwctx->device_context;
+
+ if (!s->device || !s->context) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device
or context\n");
+ return AVERROR(EINVAL);
+ }
+
+ ///< Create new hardware frames context for output
+ AVHWFramesContext *in_frames_ctx = (AVHWFramesContext
*)inl->hw_frames_ctx->data;
+ s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
+ if (!s->hw_frames_ctx_out)
+ return AVERROR(ENOMEM);
+
+ enum AVPixelFormat sw_format;
+ switch (s->output_format_opt) {
+ case OUTPUT_NV12:
+ sw_format = AV_PIX_FMT_NV12;
+ break;
+ case OUTPUT_P010:
+ sw_format = AV_PIX_FMT_P010;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ AVHWFramesContext *frames_ctx = (AVHWFramesContext
*)s->hw_frames_ctx_out->data;
+ frames_ctx->format = AV_PIX_FMT_D3D11;
+ frames_ctx->sw_format = sw_format;
+ frames_ctx->width = s->width;
+ frames_ctx->height = s->height;
+ frames_ctx->initial_pool_size = 30; ///< Adjust pool size as needed
This should have a lower default and use the extra_hw_frames option that all
filters have to add to it.
+
+ AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx;
+ frames_hwctx->MiscFlags = 0;
+ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET |
D3D11_BIND_VIDEO_ENCODER;
+
+ ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
+ if (ret < 0) {
+ av_buffer_unref(&s->hw_frames_ctx_out);
+ return ret;
+ }
+
+ outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
+ if (!outl->hw_frames_ctx)
+ return AVERROR(ENOMEM);
+
+ av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale config: %dx%d -> %dx%d\n",
+ inlink->w, inlink->h, outlink->w, outlink->h);
+ return 0;
+}
+
+static av_cold void scale_d3d11_uninit(AVFilterContext *ctx) {
+ ScaleD3D11Context *s = ctx->priv;
+
+ ///< Release D3D11 resources
+ release_d3d11_resources(s);
+
+ ///< Free the hardware device context reference
+ av_buffer_unref(&s->hw_frames_ctx_out);
+ av_buffer_unref(&s->hw_device_ctx);
+
+ ///< Free option strings
+ av_freep(&s->w_expr);
+ av_freep(&s->h_expr);
+}
+
+static const AVFilterPad scale_d3d11_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = scale_d3d11_filter_frame,
+ },
+};
+
+static const AVFilterPad scale_d3d11_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = scale_d3d11_config_props,
+ },
+};
+
+#define OFFSET(x) offsetof(ScaleD3D11Context, x)
+#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
+
+static const AVOption scale_d3d11_options[] = {
+ { "width", "Output video width", OFFSET(w_expr),
AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
+ { "height", "Output video height", OFFSET(h_expr),
AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
+ { "output_fmt", "Output format", OFFSET(output_format_opt),
AV_OPT_TYPE_INT, {.i64 = OUTPUT_NV12}, 0, OUTPUT_P010, FLAGS, "fmt" },
+ { "nv12", "NV12 format", 0, AV_OPT_TYPE_CONST, {.i64 =
OUTPUT_NV12}, 0, 0, FLAGS, "fmt" },
+ { "p010", "P010 format", 0, AV_OPT_TYPE_CONST, {.i64 =
OUTPUT_P010}, 0, 0, FLAGS, "fmt" },
+ { NULL }
There's a specific option type for that: AV_OPT_TYPE_PIXEL_FMT
+};
+
+AVFILTER_DEFINE_CLASS(scale_d3d11);
+
+const FFFilter ff_vf_scale_d3d11 = {
+ .p.name = "scale_d3d11",
+ .p.description = NULL_IF_CONFIG_SMALL("Scale video using
Direct3D11"),
+ .priv_size = sizeof(ScaleD3D11Context),
+ .p.priv_class = &scale_d3d11_class,
+ .init = scale_d3d11_init,
+ .uninit = scale_d3d11_uninit,
+ FILTER_INPUTS(scale_d3d11_inputs),
+ FILTER_OUTPUTS(scale_d3d11_outputs),
+ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
+ .p.flags = AVFILTER_FLAG_HWDEVICE,
+ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+};
\ No newline at end of file
diff --git a/libavutil/hwcontext_d3d11va.c
b/libavutil/hwcontext_d3d11va.c
index 1a047ce57b..36694896e4 100644
--- a/libavutil/hwcontext_d3d11va.c
+++ b/libavutil/hwcontext_d3d11va.c
@@ -82,6 +82,8 @@ typedef struct D3D11VAFramesContext {
int nb_surfaces;
int nb_surfaces_used;
+ int retries;
+ int max_retries;
DXGI_FORMAT format;
@@ -258,7 +260,9 @@ static AVBufferRef *d3d11va_pool_alloc(void
*opaque, size_t size)
ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
if (s->nb_surfaces_used >= texDesc.ArraySize) {
- av_log(ctx, AV_LOG_ERROR, "Static surface pool size
exceeded.\n");
+ if (s->retries >= s->max_retries) {
+ av_log(ctx, AV_LOG_ERROR, "Static surface pool size
exceeded.\n");
+ }
return NULL;
}
@@ -339,20 +343,30 @@ static int
d3d11va_frames_init(AVHWFramesContext *ctx)
static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
{
AVD3D11FrameDescriptor *desc;
+ D3D11VAFramesContext *s = ctx->hwctx;
+ s->retries = 0;
+ s->max_retries = 50;
+
+ while (s->retries < s->max_retries) {
+
+ frame->buf[0] = av_buffer_pool_get(ctx->pool);
+ if (frame->buf[0]) {
+ desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
+
+ frame->data[0] = (uint8_t *)desc->texture;
+ frame->data[1] = (uint8_t *)desc->index;
+ frame->format = AV_PIX_FMT_D3D11;
+ frame->width = ctx->width;
+ frame->height = ctx->height;
+
+ return 0;
+ }
- frame->buf[0] = av_buffer_pool_get(ctx->pool);
- if (!frame->buf[0])
- return AVERROR(ENOMEM);
-
- desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
-
- frame->data[0] = (uint8_t *)desc->texture;
- frame->data[1] = (uint8_t *)desc->index;
- frame->format = AV_PIX_FMT_D3D11;
- frame->width = ctx->width;
- frame->height = ctx->height;
+ av_usleep(1000);
+ s->retries++;
+ }
- return 0;
+ return AVERROR(ENOMEM);
}
static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
I'm afraid, but this loop is not right. Please take a look at how other
filters are handling the case when no hardware frame is available.
Thanks
sw
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org<mailto:ffmpeg-devel@ffmpeg.org>
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org<mailto:ffmpeg-devel-request@ffmpeg.org> with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-05-30 8:01 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-22 13:20 [FFmpeg-devel] [PATCH 1/2] avfilter: add scale_d3d11 filter Dash Santosh Sathyanarayanan
2025-05-22 15:25 ` Timo Rothenpieler
2025-05-22 17:57 ` Dash Santosh Sathyanarayanan
2025-05-22 18:42 ` softworkz .
2025-05-30 8:01 ` Dash Santosh Sathyanarayanan
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git