FFmpeg
Loading...
Searching...
No Matches
vf_scale_d3d12.c
Go to the documentation of this file.
1/**
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#define COBJMACROS
20
21#include "libavutil/fifo.h"
22#include "libavutil/opt.h"
23#include "libavutil/pixdesc.h"
24#include "compat/w32dlfcn.h"
25
26#include "libavutil/hwcontext.h"
29
30#include "filters.h"
31#include "scale_eval.h"
32#include "video.h"
33
38
39typedef struct ScaleD3D12Context {
41 char *w_expr;
42 char *h_expr;
44
45 /* D3D12 objects */
46 ID3D12Device *device;
47 ID3D12VideoDevice *video_device;
48 ID3D12VideoProcessor *video_processor;
49 ID3D12CommandQueue *command_queue;
50 ID3D12VideoProcessCommandList *command_list;
51
53
54 /* Synchronization */
55 ID3D12Fence *fence;
58
59 /* Buffer references */
62
63 /* Dimensions and formats */
66 DXGI_FORMAT input_format;
67 DXGI_FORMAT output_format;
68
69 /* Color space and frame rate */
70 DXGI_COLOR_SPACE_TYPE input_colorspace;
72
73 /* Video processor capabilities */
74 D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT process_support;
76
78 return 0;
79}
80
82 ID3D12CommandAllocator **ppAllocator)
83{
84 HRESULT hr;
86
87 if (av_fifo_peek(s->allocator_queue, &allocator, 1, 0) >= 0) {
88 UINT64 completed = ID3D12Fence_GetCompletedValue(s->fence);
89 if (completed >= allocator.fence_value) {
90 *ppAllocator = allocator.command_allocator;
91 av_fifo_read(s->allocator_queue, &allocator, 1);
92 return 0;
93 }
94 }
95
96 hr = ID3D12Device_CreateCommandAllocator(s->device, D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
97 &IID_ID3D12CommandAllocator, (void **)ppAllocator);
98 if (FAILED(hr)) {
99 av_log(ctx, AV_LOG_ERROR, "Failed to create a new command allocator: HRESULT 0x%lX\n", hr);
100 return AVERROR_EXTERNAL;
101 }
102
103 return 0;
104}
105
106static int scale_d3d12_discard_command_allocator(ScaleD3D12Context *s, ID3D12CommandAllocator *pAllocator,
107 UINT64 fence_value)
108{
109 D3D12ScaleCommandAllocator allocator = {
110 .command_allocator = pAllocator,
111 .fence_value = fence_value,
112 };
113
114 av_fifo_write(s->allocator_queue, &allocator, 1);
115
116 return 0;
117}
118
120 UINT64 fence_value;
121 HRESULT hr;
123
124 /* Wait for all GPU operations to complete before releasing resources */
125 if (s->command_queue && s->fence && s->fence_event) {
126 fence_value = s->fence_value - 1;
127 hr = ID3D12CommandQueue_Signal(s->command_queue, s->fence, fence_value);
128 if (SUCCEEDED(hr)) {
129 UINT64 completed = ID3D12Fence_GetCompletedValue(s->fence);
130 if (completed < fence_value) {
131 hr = ID3D12Fence_SetEventOnCompletion(s->fence, fence_value, s->fence_event);
132 if (SUCCEEDED(hr)) {
133 WaitForSingleObject(s->fence_event, INFINITE);
134 }
135 }
136 }
137 }
138
139 if (s->fence_event) {
140 CloseHandle(s->fence_event);
141 s->fence_event = NULL;
142 }
143
144 if (s->fence) {
145 ID3D12Fence_Release(s->fence);
146 s->fence = NULL;
147 }
148
149 if (s->command_list) {
150 ID3D12VideoProcessCommandList_Release(s->command_list);
151 s->command_list = NULL;
152 }
153
154 if (s->allocator_queue) {
155 while (av_fifo_read(s->allocator_queue, &allocator, 1) >= 0)
156 ID3D12CommandAllocator_Release(allocator.command_allocator);
157 av_fifo_freep2(&s->allocator_queue);
158 }
159
160 if (s->video_processor) {
161 ID3D12VideoProcessor_Release(s->video_processor);
162 s->video_processor = NULL;
163 }
164
165 if (s->video_device) {
166 ID3D12VideoDevice_Release(s->video_device);
167 s->video_device = NULL;
168 }
169
170 if (s->command_queue) {
171 ID3D12CommandQueue_Release(s->command_queue);
172 s->command_queue = NULL;
173 }
174}
175
176static DXGI_COLOR_SPACE_TYPE get_dxgi_colorspace(enum AVColorSpace colorspace, enum AVColorTransferCharacteristic trc, int is_10bit)
177{
178 /* Map FFmpeg color space to DXGI color space */
179 if (is_10bit) {
180 /* 10-bit formats (P010) */
181 if (colorspace == AVCOL_SPC_BT2020_NCL || colorspace == AVCOL_SPC_BT2020_CL) {
182 if (trc == AVCOL_TRC_SMPTE2084) {
183 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020; ///< HDR10
184 } else if (trc == AVCOL_TRC_ARIB_STD_B67) {
185 return DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020; ///< HLG
186 } else {
187 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020;
188 }
189 } else {
190 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709; ///< Rec.709 10-bit
191 }
192 } else {
193 /* 8-bit formats (NV12) */
194 if (colorspace == AVCOL_SPC_BT2020_NCL || colorspace == AVCOL_SPC_BT2020_CL) {
195 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020;
196 } else if (colorspace == AVCOL_SPC_BT470BG || colorspace == AVCOL_SPC_SMPTE170M) {
197 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
198 } else {
199 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709; ///< Default to Rec.709
200 }
201 }
202}
203
205{
206 AVRational framerate = {0, 0};
207
208 if (in->duration > 0 && inlink->time_base.num > 0 && inlink->time_base.den > 0) {
209 /*
210 * Calculate framerate from frame duration and timebase
211 * framerate = 1 / (duration * timebase)
212 */
213 av_reduce(&framerate.num, &framerate.den,
214 inlink->time_base.den, in->duration * inlink->time_base.num,
215 INT_MAX);
216 } else if (inlink->time_base.num > 0 && inlink->time_base.den > 0) {
217 /* Estimate from timebase (inverse of timebase is often the framerate) */
218 framerate.num = inlink->time_base.den;
219 framerate.den = inlink->time_base.num;
220 } else {
221 /* Default to 30fps if framerate cannot be determined */
222 framerate.num = 30;
223 framerate.den = 1;
224 av_log(ctx, AV_LOG_WARNING, "Input framerate not determinable, defaulting to 30fps\n");
225 }
226
227 return framerate;
228}
229
231 HRESULT hr;
232 int ret;
233 ID3D12CommandAllocator *command_allocator = NULL;
234
235 if (s->output_format == DXGI_FORMAT_UNKNOWN) {
236 av_log(ctx, AV_LOG_ERROR, "Output format not initialized\n");
237 return AVERROR(EINVAL);
238 }
239
240 AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
241 AVD3D12VADeviceContext *d3d12_hwctx = (AVD3D12VADeviceContext *)hwctx->hwctx;
242 s->device = d3d12_hwctx->device;
243
244 av_log(ctx, AV_LOG_VERBOSE, "Configuring D3D12 video processor: %dx%d -> %dx%d\n",
245 s->input_width, s->input_height, s->width, s->height);
246
247 hr = ID3D12Device_QueryInterface(s->device, &IID_ID3D12VideoDevice, (void **)&s->video_device);
248 if (FAILED(hr)) {
249 av_log(ctx, AV_LOG_ERROR, "Failed to get D3D12 video device interface: HRESULT 0x%lX\n", hr);
250 return AVERROR_EXTERNAL;
251 }
252
253 D3D12_COMMAND_QUEUE_DESC queue_desc = {
254 .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
255 .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
256 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
257 .NodeMask = 0
258 };
259
260 hr = ID3D12Device_CreateCommandQueue(s->device, &queue_desc, &IID_ID3D12CommandQueue, (void **)&s->command_queue);
261 if (FAILED(hr)) {
262 av_log(ctx, AV_LOG_ERROR, "Failed to create command queue: HRESULT 0x%lX\n", hr);
263 return AVERROR_EXTERNAL;
264 }
265
266 s->process_support.NodeIndex = 0;
267
268 s->process_support.InputSample.Format.Format = s->input_format;
269 s->process_support.InputSample.Format.ColorSpace = s->input_colorspace;
270 s->process_support.InputSample.Width = s->input_width;
271 s->process_support.InputSample.Height = s->input_height;
272 s->process_support.InputFrameRate.Numerator = s->input_framerate.num;
273 s->process_support.InputFrameRate.Denominator = s->input_framerate.den;
274 s->process_support.InputFieldType = D3D12_VIDEO_FIELD_TYPE_NONE;
275 s->process_support.InputStereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE;
276
277 s->process_support.OutputFormat.Format = s->output_format;
278 s->process_support.OutputFormat.ColorSpace = s->input_colorspace;
279 s->process_support.OutputFrameRate.Numerator = s->input_framerate.num;
280 s->process_support.OutputFrameRate.Denominator = s->input_framerate.den;
281 s->process_support.OutputStereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE;
282
283 hr = ID3D12VideoDevice_CheckFeatureSupport(
284 s->video_device,
285 D3D12_FEATURE_VIDEO_PROCESS_SUPPORT,
286 &s->process_support,
287 sizeof(s->process_support)
288 );
289
290 if (FAILED(hr)) {
291 av_log(ctx, AV_LOG_ERROR, "Video process feature not supported: HRESULT 0x%lX\n", hr);
292 return AVERROR_EXTERNAL;
293 }
294
295 if (!(s->process_support.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED)) {
296 av_log(ctx, AV_LOG_ERROR, "Video process configuration not supported by hardware\n");
297 return AVERROR_EXTERNAL;
298 }
299
300 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_DESC processor_output_desc = {
301 .Format = s->output_format,
302 .ColorSpace = s->input_colorspace,
303 .AlphaFillMode = D3D12_VIDEO_PROCESS_ALPHA_FILL_MODE_OPAQUE,
304 .AlphaFillModeSourceStreamIndex = 0,
305 .BackgroundColor = { 0.0f, 0.0f, 0.0f, 1.0f },
306 .FrameRate = { s->input_framerate.num, s->input_framerate.den },
307 .EnableStereo = FALSE,
308 };
309
310 D3D12_VIDEO_PROCESS_INPUT_STREAM_DESC processor_input_desc = {
311 .Format = s->input_format,
312 .ColorSpace = s->input_colorspace,
313 .SourceAspectRatio = { s->input_width, s->input_height },
314 .DestinationAspectRatio = { s->width, s->height },
315 .FrameRate = { s->input_framerate.num, s->input_framerate.den },
316 .StereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE,
317 .FieldType = D3D12_VIDEO_FIELD_TYPE_NONE,
318 .DeinterlaceMode = D3D12_VIDEO_PROCESS_DEINTERLACE_FLAG_NONE,
319 .EnableOrientation = FALSE,
320 .FilterFlags = D3D12_VIDEO_PROCESS_FILTER_FLAG_NONE,
321 .SourceSizeRange = {
322 .MaxWidth = s->input_width,
323 .MaxHeight = s->input_height,
324 .MinWidth = s->input_width,
325 .MinHeight = s->input_height
326 },
327 .DestinationSizeRange = {
328 .MaxWidth = s->width,
329 .MaxHeight = s->height,
330 .MinWidth = s->width,
331 .MinHeight = s->height
332 },
333 .EnableAlphaBlending = FALSE,
334 .LumaKey = { .Enable = FALSE, .Lower = 0.0f, .Upper = 1.0f },
335 .NumPastFrames = 0,
336 .NumFutureFrames = 0,
337 .EnableAutoProcessing = FALSE,
338 };
339
340 /* If pixel aspect ratio adjustment is not supported, set to 1:1 and warn */
341 if (!(s->process_support.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_PIXEL_ASPECT_RATIO)) {
342 processor_input_desc.SourceAspectRatio.Numerator = 1;
343 processor_input_desc.SourceAspectRatio.Denominator = 1;
344 processor_input_desc.DestinationAspectRatio.Numerator = 1;
345 processor_input_desc.DestinationAspectRatio.Denominator = 1;
346 av_log(ctx, AV_LOG_WARNING, "Pixel aspect ratio adjustment not supported by hardware\n");
347 }
348
349 hr = ID3D12VideoDevice_CreateVideoProcessor(
350 s->video_device,
351 0,
352 &processor_output_desc,
353 1,
354 &processor_input_desc,
355 &IID_ID3D12VideoProcessor,
356 (void **)&s->video_processor
357 );
358
359 if (FAILED(hr)) {
360 av_log(ctx, AV_LOG_ERROR, "Failed to create video processor: HRESULT 0x%lX\n", hr);
361 return AVERROR_EXTERNAL;
362 }
363
364 hr = ID3D12Device_CreateFence(s->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&s->fence);
365 if (FAILED(hr)) {
366 av_log(ctx, AV_LOG_ERROR, "Failed to create fence: HRESULT 0x%lX\n", hr);
367 return AVERROR_EXTERNAL;
368 }
369
370 s->fence_value = 1;
371 s->fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
372 if (!s->fence_event) {
373 av_log(ctx, AV_LOG_ERROR, "Failed to create fence event\n");
374 return AVERROR_EXTERNAL;
375 }
376
378 if (!s->allocator_queue)
379 return AVERROR(ENOMEM);
380
381 ret = scale_d3d12_get_valid_command_allocator(s, ctx, &command_allocator);
382 if (ret < 0)
383 return ret;
384
385 hr = ID3D12Device_CreateCommandList(
386 s->device,
387 0,
388 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
389 command_allocator,
390 NULL,
391 &IID_ID3D12VideoProcessCommandList,
392 (void **)&s->command_list
393 );
394
395 if (FAILED(hr)) {
396 av_log(ctx, AV_LOG_ERROR, "Failed to create command list: HRESULT 0x%lX\n", hr);
397 ID3D12CommandAllocator_Release(command_allocator);
398 return AVERROR_EXTERNAL;
399 }
400
401 ID3D12VideoProcessCommandList_Close(s->command_list);
402
403 ret = scale_d3d12_discard_command_allocator(s, command_allocator, 0);
404 if (ret < 0)
405 return ret;
406
407 av_log(ctx, AV_LOG_VERBOSE, "D3D12 video processor successfully configured\n");
408 return 0;
409}
410
412{
413 AVFilterContext *ctx = inlink->dst;
414 ScaleD3D12Context *s = ctx->priv;
415 AVFilterLink *outlink = ctx->outputs[0];
416 AVFrame *out = NULL;
417 int ret = 0;
418 HRESULT hr;
419 ID3D12CommandAllocator *command_allocator = NULL;
420
421 if (!in) {
422 av_log(ctx, AV_LOG_ERROR, "Null input frame\n");
423 return AVERROR(EINVAL);
424 }
425
426 if (!in->hw_frames_ctx) {
427 av_log(ctx, AV_LOG_ERROR, "No hardware frames context in input frame\n");
428 av_frame_free(&in);
429 return AVERROR(EINVAL);
430 }
431
433
434 if (!s->hw_device_ctx) {
435 av_log(ctx, AV_LOG_ERROR, "Filter hardware device context is uninitialized\n");
436 av_frame_free(&in);
437 return AVERROR(EINVAL);
438 }
439
440 AVHWDeviceContext *input_device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data;
441 AVHWDeviceContext *filter_device_ctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
442
443 if (input_device_ctx->type != filter_device_ctx->type) {
444 av_log(ctx, AV_LOG_ERROR, "Mismatch between input and filter hardware device types\n");
445 av_frame_free(&in);
446 return AVERROR(EINVAL);
447 }
448
450 if (!out) {
451 av_log(ctx, AV_LOG_ERROR, "Failed to allocate output frame\n");
452 av_frame_free(&in);
453 return AVERROR(ENOMEM);
454 }
455
456 ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0);
457 if (ret < 0) {
458 av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n");
459 goto fail;
460 }
461
462 if (!s->video_processor) {
463 AVHWFramesContext *input_frames_ctx = (AVHWFramesContext *)in->hw_frames_ctx->data;
464
465 s->input_width = input_frames_ctx->width;
466 s->input_height = input_frames_ctx->height;
467
468 AVD3D12VAFramesContext *input_hwctx = (AVD3D12VAFramesContext *)input_frames_ctx->hwctx;
469 s->input_format = input_hwctx->format;
470
471 if (s->input_format == DXGI_FORMAT_UNKNOWN) {
472 switch (input_frames_ctx->sw_format) {
473 case AV_PIX_FMT_NV12:
474 s->input_format = DXGI_FORMAT_NV12;
475 break;
476 case AV_PIX_FMT_P010:
477 s->input_format = DXGI_FORMAT_P010;
478 break;
479 default:
480 av_log(ctx, AV_LOG_ERROR, "Unsupported input format\n");
481 ret = AVERROR(EINVAL);
482 goto fail;
483 }
484 }
485
486 int is_10bit = (s->input_format == DXGI_FORMAT_P010);
487 s->input_colorspace = get_dxgi_colorspace(in->colorspace, in->color_trc, is_10bit);
488
489 s->input_framerate = get_input_framerate(ctx, inlink, in);
490
491 av_log(ctx, AV_LOG_VERBOSE, "Input format: %dx%d, DXGI format: %d, colorspace: %d, framerate: %d/%d\n",
492 s->input_width, s->input_height, s->input_format, s->input_colorspace,
493 s->input_framerate.num, s->input_framerate.den);
494
496 if (ret < 0) {
497 av_log(ctx, AV_LOG_ERROR, "Failed to configure processor\n");
498 goto fail;
499 }
500 }
501
502 AVD3D12VAFrame *input_frame = (AVD3D12VAFrame *)in->data[0];
504
505 if (!input_frame || !output_frame) {
506 av_log(ctx, AV_LOG_ERROR, "Invalid frame pointers\n");
507 ret = AVERROR(EINVAL);
508 goto fail;
509 }
510
511 ID3D12Resource *input_resource = input_frame->texture;
512 ID3D12Resource *output_resource = output_frame->texture;
513
514 if (!input_resource || !output_resource) {
515 av_log(ctx, AV_LOG_ERROR, "Invalid D3D12 resources in frames\n");
516 ret = AVERROR(EINVAL);
517 goto fail;
518 }
519
520 /* Wait for input frame's fence before accessing it */
521 if (input_frame->sync_ctx.fence && input_frame->sync_ctx.fence_value > 0) {
522 UINT64 completed = ID3D12Fence_GetCompletedValue(input_frame->sync_ctx.fence);
523 if (completed < input_frame->sync_ctx.fence_value) {
524 hr = ID3D12CommandQueue_Wait(s->command_queue, input_frame->sync_ctx.fence, input_frame->sync_ctx.fence_value);
525 if (FAILED(hr)) {
526 av_log(ctx, AV_LOG_ERROR, "Failed to wait for input fence: HRESULT 0x%lX\n", hr);
527 ret = AVERROR_EXTERNAL;
528 goto fail;
529 }
530 }
531 }
532
533 ret = scale_d3d12_get_valid_command_allocator(s, ctx, &command_allocator);
534 if (ret < 0)
535 goto fail;
536
537 hr = ID3D12CommandAllocator_Reset(command_allocator);
538 if (FAILED(hr)) {
539 av_log(ctx, AV_LOG_ERROR, "Failed to reset command allocator: HRESULT 0x%lX\n", hr);
540 ret = AVERROR_EXTERNAL;
541 goto fail;
542 }
543
544 hr = ID3D12VideoProcessCommandList_Reset(s->command_list, command_allocator);
545 if (FAILED(hr)) {
546 av_log(ctx, AV_LOG_ERROR, "Failed to reset command list: HRESULT 0x%lX\n", hr);
547 ret = AVERROR_EXTERNAL;
548 goto fail;
549 }
550
551 D3D12_RESOURCE_BARRIER barriers[2] = {
552 {
553 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
554 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
555 .Transition = {
556 .pResource = input_resource,
557 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
558 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
559 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_PROCESS_READ
560 }
561 },
562 {
563 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
564 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
565 .Transition = {
566 .pResource = output_resource,
567 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
568 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
569 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE
570 }
571 }
572 };
573
574 ID3D12VideoProcessCommandList_ResourceBarrier(s->command_list, 2, barriers);
575
576 D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS input_args = {0};
577
578 input_args.InputStream[0].pTexture2D = input_resource;
579 input_args.Transform.SourceRectangle.right = s->input_width;
580 input_args.Transform.SourceRectangle.bottom = s->input_height;
581 input_args.Transform.DestinationRectangle.right = s->width;
582 input_args.Transform.DestinationRectangle.bottom = s->height;
583 input_args.Transform.Orientation = D3D12_VIDEO_PROCESS_ORIENTATION_DEFAULT;
584
585 input_args.Flags = D3D12_VIDEO_PROCESS_INPUT_STREAM_FLAG_NONE;
586
587 input_args.RateInfo.OutputIndex = 0;
588 input_args.RateInfo.InputFrameOrField = 0;
589
590 memset(input_args.FilterLevels, 0, sizeof(input_args.FilterLevels));
591
592 input_args.AlphaBlending.Enable = FALSE;
593 input_args.AlphaBlending.Alpha = 1.0f;
594
595 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_ARGUMENTS output_args = {0};
596
597 output_args.OutputStream[0].pTexture2D = output_resource;
598 output_args.TargetRectangle.right = s->width;
599 output_args.TargetRectangle.bottom = s->height;
600
601 ID3D12VideoProcessCommandList_ProcessFrames(
602 s->command_list,
603 s->video_processor,
604 &output_args,
605 1,
606 &input_args
607 );
608
609 for (int i = 0; i < 2; i++) {
610 FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
611 }
612 ID3D12VideoProcessCommandList_ResourceBarrier(s->command_list, 2, barriers);
613
614 hr = ID3D12VideoProcessCommandList_Close(s->command_list);
615 if (FAILED(hr)) {
616 av_log(ctx, AV_LOG_ERROR, "Failed to close command list: HRESULT 0x%lX\n", hr);
617 ret = AVERROR_EXTERNAL;
618 goto fail;
619 }
620
621 ID3D12CommandList *cmd_lists[] = { (ID3D12CommandList *)s->command_list };
622 ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, cmd_lists);
623
624 hr = ID3D12CommandQueue_Signal(s->command_queue, s->fence, s->fence_value);
625 if (FAILED(hr)) {
626 av_log(ctx, AV_LOG_ERROR, "Failed to signal fence: HRESULT 0x%lX\n", hr);
627 ret = AVERROR_EXTERNAL;
628 goto fail;
629 }
630
631 output_frame->sync_ctx.fence = s->fence;
632 output_frame->sync_ctx.fence_value = s->fence_value;
633 ID3D12Fence_AddRef(s->fence); ///< Increment reference count
634
635 ret = scale_d3d12_discard_command_allocator(s, command_allocator, s->fence_value);
636 command_allocator = NULL;
637 if (ret < 0)
638 goto fail;
639
640 s->fence_value++;
641
642 ret = av_frame_copy_props(out, in);
643 if (ret < 0) {
644 av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
645 goto fail;
646 }
647
648 out->width = s->width;
649 out->height = s->height;
650 out->format = AV_PIX_FMT_D3D12;
651
652 av_frame_free(&in);
653
654 return ff_filter_frame(outlink, out);
655
656fail:
657 if (command_allocator)
658 scale_d3d12_discard_command_allocator(s, command_allocator, s->fence_value);
659 av_frame_free(&in);
661 return ret;
662}
663
665{
666 AVFilterContext *ctx = outlink->src;
667 ScaleD3D12Context *s = ctx->priv;
668 AVFilterLink *inlink = ctx->inputs[0];
669 FilterLink *inl = ff_filter_link(inlink);
670 FilterLink *outl = ff_filter_link(outlink);
671 int ret;
672
674
675 av_buffer_unref(&s->hw_frames_ctx_out);
676 av_buffer_unref(&s->hw_device_ctx);
677
678 ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &s->width, &s->height);
679 if (ret < 0) {
680 av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
681 return ret;
682 }
683
684 /* Adjust dimensions to meet codec/hardware alignment requirements */
685 ret = ff_scale_adjust_dimensions(inlink, &s->width, &s->height, 0, 1, 1.f);
686 if (ret < 0)
687 return ret;
688
689 outlink->w = s->width;
690 outlink->h = s->height;
691
692 if (!inl->hw_frames_ctx) {
693 av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on input link\n");
694 return AVERROR(EINVAL);
695 }
696
697 if (!s->hw_device_ctx) {
698 AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
699 s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
700 if (!s->hw_device_ctx) {
701 av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter hardware device context\n");
702 return AVERROR(ENOMEM);
703 }
704 }
705
706 AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
707 AVD3D12VADeviceContext *d3d12_hwctx = (AVD3D12VADeviceContext *)hwctx->hwctx;
708
709 s->device = d3d12_hwctx->device;
710
711 if (!s->device) {
712 av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D12 device\n");
713 return AVERROR(EINVAL);
714 }
715
716 s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
717 if (!s->hw_frames_ctx_out)
718 return AVERROR(ENOMEM);
719
720 AVHWFramesContext *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data;
721 AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
722
723 if (s->format == AV_PIX_FMT_NONE) {
724 /* If format is not specified, use the same format as input */
725 frames_ctx->sw_format = in_frames_ctx->sw_format;
726 s->format = in_frames_ctx->sw_format;
727 av_log(ctx, AV_LOG_VERBOSE, "D3D12 scale output format not specified, using input format: %s\n",
728 av_get_pix_fmt_name(s->format));
729 } else {
730 frames_ctx->sw_format = s->format;
731 }
732
733 /* Set output format based on sw_format */
734 switch (frames_ctx->sw_format) {
735 case AV_PIX_FMT_NV12:
736 s->output_format = DXGI_FORMAT_NV12;
737 break;
738 case AV_PIX_FMT_P010:
739 s->output_format = DXGI_FORMAT_P010;
740 break;
741 default:
742 av_log(ctx, AV_LOG_ERROR, "Unsupported output format: %s\n",
743 av_get_pix_fmt_name(frames_ctx->sw_format));
744 av_buffer_unref(&s->hw_frames_ctx_out);
745 return AVERROR(EINVAL);
746 }
747
748 frames_ctx->width = s->width;
749 frames_ctx->height = s->height;
750 frames_ctx->format = AV_PIX_FMT_D3D12;
751 frames_ctx->initial_pool_size = 10;
752
753 if (ctx->extra_hw_frames > 0)
754 frames_ctx->initial_pool_size += ctx->extra_hw_frames;
755
756 AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
757
758 /*
759 * Set D3D12 resource flags for video processing
760 * ALLOW_RENDER_TARGET is needed for video processor output
761 */
762 frames_hwctx->format = s->output_format;
763 frames_hwctx->resource_flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
764 frames_hwctx->heap_flags = D3D12_HEAP_FLAG_NONE;
765
766 ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
767 if (ret < 0) {
768 av_buffer_unref(&s->hw_frames_ctx_out);
769 return ret;
770 }
771
772 outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
773 if (!outl->hw_frames_ctx)
774 return AVERROR(ENOMEM);
775
776 av_log(ctx, AV_LOG_VERBOSE, "D3D12 scale config: %dx%d -> %dx%d\n",
777 inlink->w, inlink->h, outlink->w, outlink->h);
778 return 0;
779}
780
782 ScaleD3D12Context *s = ctx->priv;
783
785
786 av_buffer_unref(&s->hw_frames_ctx_out);
787 av_buffer_unref(&s->hw_device_ctx);
788
789 av_freep(&s->w_expr);
790 av_freep(&s->h_expr);
791}
792
794 {
795 .name = "default",
796 .type = AVMEDIA_TYPE_VIDEO,
797 .filter_frame = scale_d3d12_filter_frame,
798 },
799};
800
802 {
803 .name = "default",
804 .type = AVMEDIA_TYPE_VIDEO,
805 .config_props = scale_d3d12_config_props,
806 },
807};
808
809#define OFFSET(x) offsetof(ScaleD3D12Context, x)
810#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
811
813 { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
814 { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
815 { "format", "Output video pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, INT_MIN, INT_MAX, .flags=FLAGS },
816 { NULL }
817};
818
820
822 .p.name = "scale_d3d12",
823 .p.description = NULL_IF_CONFIG_SMALL("Scale video using Direct3D12"),
824 .priv_size = sizeof(ScaleD3D12Context),
825 .p.priv_class = &scale_d3d12_class,
826 .init = scale_d3d12_init,
827 .uninit = scale_d3d12_uninit,
831 .p.flags = AVFILTER_FLAG_HWDEVICE,
832 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
833};
static const char *const format[]
Definition af_aiir.c:445
const FFFilter ff_vf_scale_d3d12
static FILE * out
static AVFormatContext * ctx
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1076
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:596
#define NULL
Definition coverity.c:32
A generic FIFO API.
#define fail
Definition test.h:479
@ AV_OPT_TYPE_PIXEL_FMT
Underlying C type is enum AVPixelFormat.
Definition opt.h:306
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
#define AVFILTER_FLAG_HWDEVICE
The filter can create hardware frames using AVFilterContext.hw_device_ctx.
Definition avfilter.h:193
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:140
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:104
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR(e)
Definition error.h:45
AVFifo * av_fifo_alloc2(size_t nb_elems, size_t elem_size, unsigned int flags)
Allocate and initialize an AVFifo with a given element size.
Definition fifo.c:47
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition fifo.c:286
#define AV_FIFO_FLAG_AUTO_GROW
Automatically resize the FIFO on writes, so that the data fits.
Definition fifo.h:63
int av_fifo_peek(const AVFifo *f, void *buf, size_t nb_elems, size_t offset)
Read data from a FIFO without modifying FIFO state.
Definition fifo.c:255
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition fifo.c:188
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition fifo.c:240
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition rational.c:35
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition hwcontext.c:337
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition hwcontext.c:263
int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags)
Allocate a new frame attached to the given AVHWFramesContext.
Definition hwcontext.c:506
An API-specific header for AV_HWDEVICE_TYPE_D3D12VA.
static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp)
Definition h264dec.c:945
#define FILTER_INPUTS(array)
Definition filters.h:279
#define FILTER_OUTPUTS(array)
Definition filters.h:280
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:269
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:493
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:97
#define FFSWAP(type, a, b)
Definition macros.h:52
AVOptions.
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition pixdesc.c:3404
#define AV_PIX_FMT_P010
Definition pixfmt.h:613
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:96
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_D3D12
Hardware surfaces for Direct3D 12.
Definition pixfmt.h:440
AVColorTransferCharacteristic
Color Transfer Characteristic.
Definition pixfmt.h:677
@ AVCOL_TRC_SMPTE2084
SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems.
Definition pixfmt.h:694
@ AVCOL_TRC_ARIB_STD_B67
ARIB STD-B67, known as "Hybrid log-gamma".
Definition pixfmt.h:698
AVColorSpace
YUV colorspace type.
Definition pixfmt.h:711
@ AVCOL_SPC_BT470BG
also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
Definition pixfmt.h:717
@ AVCOL_SPC_BT2020_CL
ITU-R BT2020 constant luminance system.
Definition pixfmt.h:723
@ AVCOL_SPC_BT2020_NCL
ITU-R BT2020 non-constant luminance system.
Definition pixfmt.h:722
@ AVCOL_SPC_SMPTE170M
also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
Definition pixfmt.h:718
int ff_scale_adjust_dimensions(AVFilterLink *inlink, int *ret_w, int *ret_h, int force_original_aspect_ratio, int force_divisible_by, double w_adj)
Transform evaluated width and height obtained from ff_scale_eval_dimensions into actual target width ...
Definition scale_eval.c:123
int ff_scale_eval_dimensions(void *log_ctx, const char *w_expr, const char *h_expr, AVFilterLink *inlink, AVFilterLink *outlink, int *ret_w, int *ret_h)
Parse and evaluate string expressions for width and height.
Definition scale_eval.c:58
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
Describe the class of an AVClass context structure.
Definition log.h:76
This struct is allocated as AVHWDeviceContext.hwctx.
ID3D12Device * device
Device used for objects creation and access.
D3D12VA frame descriptor for pool allocation.
ID3D12Resource * texture
The texture in which the frame is located.
AVD3D12VASyncContext sync_ctx
The sync context for the texture.
This struct is allocated as AVHWFramesContext.hwctx.
D3D12_RESOURCE_FLAGS resource_flags
Options for working with resources.
D3D12_HEAP_FLAGS heap_flags
Options for working with heaps allocation when creating resources.
DXGI_FORMAT format
DXGI_FORMAT format.
ID3D12Fence * fence
D3D12 fence object.
uint64_t fence_value
The fence value used for sync.
Definition fifo.c:35
An instance of a filter.
Definition avfilter.h:279
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:479
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:500
AVBufferRef * hw_frames_ctx
For hwaccel-format frames, this should be a reference to the AVHWFramesContext describing the frame.
Definition frame.h:776
enum AVColorSpace colorspace
YUV colorspace type.
Definition frame.h:741
enum AVColorTransferCharacteristic color_trc
Definition frame.h:734
int64_t duration
Duration of the frame, in the same units as pts.
Definition frame.h:827
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition hwcontext.h:63
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition hwcontext.h:88
enum AVHWDeviceType type
This field identifies the underlying API used for hardware access.
Definition hwcontext.h:75
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition hwcontext.h:200
AVBufferRef * device_ref
A reference to the parent AVHWDeviceContext.
Definition hwcontext.h:129
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition hwcontext.h:153
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition hwcontext.h:213
int initial_pool_size
Initial size of the frame pool.
Definition hwcontext.h:190
int width
The allocated dimensions of the frames in this pool.
Definition hwcontext.h:220
AVOption.
Definition opt.h:428
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
ID3D12CommandAllocator * command_allocator
ID3D12VideoProcessor * video_processor
AVBufferRef * hw_device_ctx
ID3D12VideoDevice * video_device
DXGI_FORMAT output_format
enum AVPixelFormat format
AVRational input_framerate
AVBufferRef * hw_frames_ctx_out
ID3D12Device * device
ID3D12VideoProcessCommandList * command_list
D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT process_support
DXGI_FORMAT input_format
ID3D12Fence * fence
ID3D12CommandQueue * command_queue
DXGI_COLOR_SPACE_TYPE input_colorspace
const AVClass * classCtx
#define av_freep(p)
#define av_log(a,...)
float framerate
Definition av1_levels.c:29
static const AVFilterPad scale_d3d12_inputs[]
static int scale_d3d12_filter_frame(AVFilterLink *inlink, AVFrame *in)
static av_cold int scale_d3d12_init(AVFilterContext *ctx)
static AVRational get_input_framerate(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in)
static av_cold void scale_d3d12_uninit(AVFilterContext *ctx)
static const AVFilterPad scale_d3d12_outputs[]
static int scale_d3d12_config_props(AVFilterLink *outlink)
static const AVOption scale_d3d12_options[]
static int scale_d3d12_get_valid_command_allocator(ScaleD3D12Context *s, AVFilterContext *ctx, ID3D12CommandAllocator **ppAllocator)
#define OFFSET(x)
static DXGI_COLOR_SPACE_TYPE get_dxgi_colorspace(enum AVColorSpace colorspace, enum AVColorTransferCharacteristic trc, int is_10bit)
static int scale_d3d12_discard_command_allocator(ScaleD3D12Context *s, ID3D12CommandAllocator *pAllocator, UINT64 fence_value)
static void release_d3d12_resources(ScaleD3D12Context *s)
static int scale_d3d12_configure_processor(ScaleD3D12Context *s, AVFilterContext *ctx)
#define WaitForSingleObject(a, b)
Definition w32pthreads.h:64