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/opt.h"
22#include "libavutil/pixdesc.h"
23#include "compat/w32dlfcn.h"
24
25#include "libavutil/hwcontext.h"
28
29#include "filters.h"
30#include "scale_eval.h"
31#include "video.h"
32
33typedef struct ScaleD3D12Context {
35 char *w_expr;
36 char *h_expr;
38
39 /* D3D12 objects */
40 ID3D12Device *device;
41 ID3D12VideoDevice *video_device;
42 ID3D12VideoProcessor *video_processor;
43 ID3D12CommandQueue *command_queue;
44 ID3D12VideoProcessCommandList *command_list;
45 ID3D12CommandAllocator *command_allocator;
46
47 /* Synchronization */
48 ID3D12Fence *fence;
51
52 /* Buffer references */
55
56 /* Dimensions and formats */
59 DXGI_FORMAT input_format;
60 DXGI_FORMAT output_format;
61
62 /* Color space and frame rate */
63 DXGI_COLOR_SPACE_TYPE input_colorspace;
65
66 /* Video processor capabilities */
67 D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT process_support;
69
71 return 0;
72}
73
75 UINT64 fence_value;
76 HRESULT hr;
77 /* Wait for all GPU operations to complete before releasing resources */
78 if (s->command_queue && s->fence && s->fence_event) {
79 fence_value = s->fence_value - 1;
80 hr = ID3D12CommandQueue_Signal(s->command_queue, s->fence, fence_value);
81 if (SUCCEEDED(hr)) {
82 UINT64 completed = ID3D12Fence_GetCompletedValue(s->fence);
83 if (completed < fence_value) {
84 hr = ID3D12Fence_SetEventOnCompletion(s->fence, fence_value, s->fence_event);
85 if (SUCCEEDED(hr)) {
86 WaitForSingleObject(s->fence_event, INFINITE);
87 }
88 }
89 }
90 }
91
92 if (s->fence_event) {
93 CloseHandle(s->fence_event);
94 s->fence_event = NULL;
95 }
96
97 if (s->fence) {
98 ID3D12Fence_Release(s->fence);
99 s->fence = NULL;
100 }
101
102 if (s->command_list) {
103 ID3D12VideoProcessCommandList_Release(s->command_list);
104 s->command_list = NULL;
105 }
106
107 if (s->command_allocator) {
108 ID3D12CommandAllocator_Release(s->command_allocator);
109 s->command_allocator = NULL;
110 }
111
112 if (s->video_processor) {
113 ID3D12VideoProcessor_Release(s->video_processor);
114 s->video_processor = NULL;
115 }
116
117 if (s->video_device) {
118 ID3D12VideoDevice_Release(s->video_device);
119 s->video_device = NULL;
120 }
121
122 if (s->command_queue) {
123 ID3D12CommandQueue_Release(s->command_queue);
124 s->command_queue = NULL;
125 }
126}
127
128static DXGI_COLOR_SPACE_TYPE get_dxgi_colorspace(enum AVColorSpace colorspace, enum AVColorTransferCharacteristic trc, int is_10bit)
129{
130 /* Map FFmpeg color space to DXGI color space */
131 if (is_10bit) {
132 /* 10-bit formats (P010) */
133 if (colorspace == AVCOL_SPC_BT2020_NCL || colorspace == AVCOL_SPC_BT2020_CL) {
134 if (trc == AVCOL_TRC_SMPTE2084) {
135 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020; ///< HDR10
136 } else if (trc == AVCOL_TRC_ARIB_STD_B67) {
137 return DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020; ///< HLG
138 } else {
139 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020;
140 }
141 } else {
142 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709; ///< Rec.709 10-bit
143 }
144 } else {
145 /* 8-bit formats (NV12) */
146 if (colorspace == AVCOL_SPC_BT2020_NCL || colorspace == AVCOL_SPC_BT2020_CL) {
147 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020;
148 } else if (colorspace == AVCOL_SPC_BT470BG || colorspace == AVCOL_SPC_SMPTE170M) {
149 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
150 } else {
151 return DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709; ///< Default to Rec.709
152 }
153 }
154}
155
157{
158 AVRational framerate = {0, 0};
159
160 if (in->duration > 0 && inlink->time_base.num > 0 && inlink->time_base.den > 0) {
161 /*
162 * Calculate framerate from frame duration and timebase
163 * framerate = 1 / (duration * timebase)
164 */
165 av_reduce(&framerate.num, &framerate.den,
166 inlink->time_base.den, in->duration * inlink->time_base.num,
167 INT_MAX);
168 } else if (inlink->time_base.num > 0 && inlink->time_base.den > 0) {
169 /* Estimate from timebase (inverse of timebase is often the framerate) */
170 framerate.num = inlink->time_base.den;
171 framerate.den = inlink->time_base.num;
172 } else {
173 /* Default to 30fps if framerate cannot be determined */
174 framerate.num = 30;
175 framerate.den = 1;
176 av_log(ctx, AV_LOG_WARNING, "Input framerate not determinable, defaulting to 30fps\n");
177 }
178
179 return framerate;
180}
181
183 HRESULT hr;
184
185 if (s->output_format == DXGI_FORMAT_UNKNOWN) {
186 av_log(ctx, AV_LOG_ERROR, "Output format not initialized\n");
187 return AVERROR(EINVAL);
188 }
189
190 AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
191 AVD3D12VADeviceContext *d3d12_hwctx = (AVD3D12VADeviceContext *)hwctx->hwctx;
192 s->device = d3d12_hwctx->device;
193
194 av_log(ctx, AV_LOG_VERBOSE, "Configuring D3D12 video processor: %dx%d -> %dx%d\n",
195 s->input_width, s->input_height, s->width, s->height);
196
197 hr = ID3D12Device_QueryInterface(s->device, &IID_ID3D12VideoDevice, (void **)&s->video_device);
198 if (FAILED(hr)) {
199 av_log(ctx, AV_LOG_ERROR, "Failed to get D3D12 video device interface: HRESULT 0x%lX\n", hr);
200 return AVERROR_EXTERNAL;
201 }
202
203 D3D12_COMMAND_QUEUE_DESC queue_desc = {
204 .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
205 .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
206 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
207 .NodeMask = 0
208 };
209
210 hr = ID3D12Device_CreateCommandQueue(s->device, &queue_desc, &IID_ID3D12CommandQueue, (void **)&s->command_queue);
211 if (FAILED(hr)) {
212 av_log(ctx, AV_LOG_ERROR, "Failed to create command queue: HRESULT 0x%lX\n", hr);
213 return AVERROR_EXTERNAL;
214 }
215
216 s->process_support.NodeIndex = 0;
217
218 s->process_support.InputSample.Format.Format = s->input_format;
219 s->process_support.InputSample.Format.ColorSpace = s->input_colorspace;
220 s->process_support.InputSample.Width = s->input_width;
221 s->process_support.InputSample.Height = s->input_height;
222 s->process_support.InputFrameRate.Numerator = s->input_framerate.num;
223 s->process_support.InputFrameRate.Denominator = s->input_framerate.den;
224 s->process_support.InputFieldType = D3D12_VIDEO_FIELD_TYPE_NONE;
225 s->process_support.InputStereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE;
226
227 s->process_support.OutputFormat.Format = s->output_format;
228 s->process_support.OutputFormat.ColorSpace = s->input_colorspace;
229 s->process_support.OutputFrameRate.Numerator = s->input_framerate.num;
230 s->process_support.OutputFrameRate.Denominator = s->input_framerate.den;
231 s->process_support.OutputStereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE;
232
233 hr = ID3D12VideoDevice_CheckFeatureSupport(
234 s->video_device,
235 D3D12_FEATURE_VIDEO_PROCESS_SUPPORT,
236 &s->process_support,
237 sizeof(s->process_support)
238 );
239
240 if (FAILED(hr)) {
241 av_log(ctx, AV_LOG_ERROR, "Video process feature not supported: HRESULT 0x%lX\n", hr);
242 return AVERROR_EXTERNAL;
243 }
244
245 if (!(s->process_support.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED)) {
246 av_log(ctx, AV_LOG_ERROR, "Video process configuration not supported by hardware\n");
247 return AVERROR_EXTERNAL;
248 }
249
250 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_DESC processor_output_desc = {
251 .Format = s->output_format,
252 .ColorSpace = s->input_colorspace,
253 .AlphaFillMode = D3D12_VIDEO_PROCESS_ALPHA_FILL_MODE_OPAQUE,
254 .AlphaFillModeSourceStreamIndex = 0,
255 .BackgroundColor = { 0.0f, 0.0f, 0.0f, 1.0f },
256 .FrameRate = { s->input_framerate.num, s->input_framerate.den },
257 .EnableStereo = FALSE,
258 };
259
260 D3D12_VIDEO_PROCESS_INPUT_STREAM_DESC processor_input_desc = {
261 .Format = s->input_format,
262 .ColorSpace = s->input_colorspace,
263 .SourceAspectRatio = { s->input_width, s->input_height },
264 .DestinationAspectRatio = { s->width, s->height },
265 .FrameRate = { s->input_framerate.num, s->input_framerate.den },
266 .StereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE,
267 .FieldType = D3D12_VIDEO_FIELD_TYPE_NONE,
268 .DeinterlaceMode = D3D12_VIDEO_PROCESS_DEINTERLACE_FLAG_NONE,
269 .EnableOrientation = FALSE,
270 .FilterFlags = D3D12_VIDEO_PROCESS_FILTER_FLAG_NONE,
271 .SourceSizeRange = {
272 .MaxWidth = s->input_width,
273 .MaxHeight = s->input_height,
274 .MinWidth = s->input_width,
275 .MinHeight = s->input_height
276 },
277 .DestinationSizeRange = {
278 .MaxWidth = s->width,
279 .MaxHeight = s->height,
280 .MinWidth = s->width,
281 .MinHeight = s->height
282 },
283 .EnableAlphaBlending = FALSE,
284 .LumaKey = { .Enable = FALSE, .Lower = 0.0f, .Upper = 1.0f },
285 .NumPastFrames = 0,
286 .NumFutureFrames = 0,
287 .EnableAutoProcessing = FALSE,
288 };
289
290 /* If pixel aspect ratio adjustment is not supported, set to 1:1 and warn */
291 if (!(s->process_support.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_PIXEL_ASPECT_RATIO)) {
292 processor_input_desc.SourceAspectRatio.Numerator = 1;
293 processor_input_desc.SourceAspectRatio.Denominator = 1;
294 processor_input_desc.DestinationAspectRatio.Numerator = 1;
295 processor_input_desc.DestinationAspectRatio.Denominator = 1;
296 av_log(ctx, AV_LOG_WARNING, "Pixel aspect ratio adjustment not supported by hardware\n");
297 }
298
299 hr = ID3D12VideoDevice_CreateVideoProcessor(
300 s->video_device,
301 0,
302 &processor_output_desc,
303 1,
304 &processor_input_desc,
305 &IID_ID3D12VideoProcessor,
306 (void **)&s->video_processor
307 );
308
309 if (FAILED(hr)) {
310 av_log(ctx, AV_LOG_ERROR, "Failed to create video processor: HRESULT 0x%lX\n", hr);
311 return AVERROR_EXTERNAL;
312 }
313
314 hr = ID3D12Device_CreateCommandAllocator(
315 s->device,
316 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
317 &IID_ID3D12CommandAllocator,
318 (void **)&s->command_allocator
319 );
320
321 if (FAILED(hr)) {
322 av_log(ctx, AV_LOG_ERROR, "Failed to create command allocator: HRESULT 0x%lX\n", hr);
323 return AVERROR_EXTERNAL;
324 }
325
326 hr = ID3D12Device_CreateCommandList(
327 s->device,
328 0,
329 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
330 s->command_allocator,
331 NULL,
332 &IID_ID3D12VideoProcessCommandList,
333 (void **)&s->command_list
334 );
335
336 if (FAILED(hr)) {
337 av_log(ctx, AV_LOG_ERROR, "Failed to create command list: HRESULT 0x%lX\n", hr);
338 return AVERROR_EXTERNAL;
339 }
340
341 ID3D12VideoProcessCommandList_Close(s->command_list);
342
343 hr = ID3D12Device_CreateFence(s->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&s->fence);
344 if (FAILED(hr)) {
345 av_log(ctx, AV_LOG_ERROR, "Failed to create fence: HRESULT 0x%lX\n", hr);
346 return AVERROR_EXTERNAL;
347 }
348
349 s->fence_value = 1;
350 s->fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
351 if (!s->fence_event) {
352 av_log(ctx, AV_LOG_ERROR, "Failed to create fence event\n");
353 return AVERROR_EXTERNAL;
354 }
355
356 av_log(ctx, AV_LOG_VERBOSE, "D3D12 video processor successfully configured\n");
357 return 0;
358}
359
361{
362 AVFilterContext *ctx = inlink->dst;
363 ScaleD3D12Context *s = ctx->priv;
364 AVFilterLink *outlink = ctx->outputs[0];
365 AVFrame *out = NULL;
366 int ret = 0;
367 HRESULT hr;
368
369 if (!in) {
370 av_log(ctx, AV_LOG_ERROR, "Null input frame\n");
371 return AVERROR(EINVAL);
372 }
373
374 if (!in->hw_frames_ctx) {
375 av_log(ctx, AV_LOG_ERROR, "No hardware frames context in input frame\n");
376 av_frame_free(&in);
377 return AVERROR(EINVAL);
378 }
379
381
382 if (!s->hw_device_ctx) {
383 av_log(ctx, AV_LOG_ERROR, "Filter hardware device context is uninitialized\n");
384 av_frame_free(&in);
385 return AVERROR(EINVAL);
386 }
387
388 AVHWDeviceContext *input_device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data;
389 AVHWDeviceContext *filter_device_ctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
390
391 if (input_device_ctx->type != filter_device_ctx->type) {
392 av_log(ctx, AV_LOG_ERROR, "Mismatch between input and filter hardware device types\n");
393 av_frame_free(&in);
394 return AVERROR(EINVAL);
395 }
396
398 if (!out) {
399 av_log(ctx, AV_LOG_ERROR, "Failed to allocate output frame\n");
400 av_frame_free(&in);
401 return AVERROR(ENOMEM);
402 }
403
404 ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0);
405 if (ret < 0) {
406 av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n");
407 goto fail;
408 }
409
410 if (!s->video_processor) {
411 AVHWFramesContext *input_frames_ctx = (AVHWFramesContext *)in->hw_frames_ctx->data;
412
413 s->input_width = input_frames_ctx->width;
414 s->input_height = input_frames_ctx->height;
415
416 AVD3D12VAFramesContext *input_hwctx = (AVD3D12VAFramesContext *)input_frames_ctx->hwctx;
417 s->input_format = input_hwctx->format;
418
419 if (s->input_format == DXGI_FORMAT_UNKNOWN) {
420 switch (input_frames_ctx->sw_format) {
421 case AV_PIX_FMT_NV12:
422 s->input_format = DXGI_FORMAT_NV12;
423 break;
424 case AV_PIX_FMT_P010:
425 s->input_format = DXGI_FORMAT_P010;
426 break;
427 default:
428 av_log(ctx, AV_LOG_ERROR, "Unsupported input format\n");
429 ret = AVERROR(EINVAL);
430 goto fail;
431 }
432 }
433
434 int is_10bit = (s->input_format == DXGI_FORMAT_P010);
435 s->input_colorspace = get_dxgi_colorspace(in->colorspace, in->color_trc, is_10bit);
436
437 s->input_framerate = get_input_framerate(ctx, inlink, in);
438
439 av_log(ctx, AV_LOG_VERBOSE, "Input format: %dx%d, DXGI format: %d, colorspace: %d, framerate: %d/%d\n",
440 s->input_width, s->input_height, s->input_format, s->input_colorspace,
441 s->input_framerate.num, s->input_framerate.den);
442
444 if (ret < 0) {
445 av_log(ctx, AV_LOG_ERROR, "Failed to configure processor\n");
446 goto fail;
447 }
448 }
449
450 AVD3D12VAFrame *input_frame = (AVD3D12VAFrame *)in->data[0];
452
453 if (!input_frame || !output_frame) {
454 av_log(ctx, AV_LOG_ERROR, "Invalid frame pointers\n");
455 ret = AVERROR(EINVAL);
456 goto fail;
457 }
458
459 ID3D12Resource *input_resource = input_frame->texture;
460 ID3D12Resource *output_resource = output_frame->texture;
461
462 if (!input_resource || !output_resource) {
463 av_log(ctx, AV_LOG_ERROR, "Invalid D3D12 resources in frames\n");
464 ret = AVERROR(EINVAL);
465 goto fail;
466 }
467
468 /* Wait for input frame's fence before accessing it */
469 if (input_frame->sync_ctx.fence && input_frame->sync_ctx.fence_value > 0) {
470 UINT64 completed = ID3D12Fence_GetCompletedValue(input_frame->sync_ctx.fence);
471 if (completed < input_frame->sync_ctx.fence_value) {
472 hr = ID3D12CommandQueue_Wait(s->command_queue, input_frame->sync_ctx.fence, input_frame->sync_ctx.fence_value);
473 if (FAILED(hr)) {
474 av_log(ctx, AV_LOG_ERROR, "Failed to wait for input fence: HRESULT 0x%lX\n", hr);
475 ret = AVERROR_EXTERNAL;
476 goto fail;
477 }
478 }
479 }
480
481 hr = ID3D12CommandAllocator_Reset(s->command_allocator);
482 if (FAILED(hr)) {
483 av_log(ctx, AV_LOG_ERROR, "Failed to reset command allocator: HRESULT 0x%lX\n", hr);
484 ret = AVERROR_EXTERNAL;
485 goto fail;
486 }
487
488 hr = ID3D12VideoProcessCommandList_Reset(s->command_list, s->command_allocator);
489 if (FAILED(hr)) {
490 av_log(ctx, AV_LOG_ERROR, "Failed to reset command list: HRESULT 0x%lX\n", hr);
491 ret = AVERROR_EXTERNAL;
492 goto fail;
493 }
494
495 D3D12_RESOURCE_BARRIER barriers[2] = {
496 {
497 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
498 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
499 .Transition = {
500 .pResource = input_resource,
501 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
502 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
503 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_PROCESS_READ
504 }
505 },
506 {
507 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
508 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
509 .Transition = {
510 .pResource = output_resource,
511 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
512 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
513 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE
514 }
515 }
516 };
517
518 ID3D12VideoProcessCommandList_ResourceBarrier(s->command_list, 2, barriers);
519
520 D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS input_args = {0};
521
522 input_args.InputStream[0].pTexture2D = input_resource;
523 input_args.Transform.SourceRectangle.right = s->input_width;
524 input_args.Transform.SourceRectangle.bottom = s->input_height;
525 input_args.Transform.DestinationRectangle.right = s->width;
526 input_args.Transform.DestinationRectangle.bottom = s->height;
527 input_args.Transform.Orientation = D3D12_VIDEO_PROCESS_ORIENTATION_DEFAULT;
528
529 input_args.Flags = D3D12_VIDEO_PROCESS_INPUT_STREAM_FLAG_NONE;
530
531 input_args.RateInfo.OutputIndex = 0;
532 input_args.RateInfo.InputFrameOrField = 0;
533
534 memset(input_args.FilterLevels, 0, sizeof(input_args.FilterLevels));
535
536 input_args.AlphaBlending.Enable = FALSE;
537 input_args.AlphaBlending.Alpha = 1.0f;
538
539 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_ARGUMENTS output_args = {0};
540
541 output_args.OutputStream[0].pTexture2D = output_resource;
542 output_args.TargetRectangle.right = s->width;
543 output_args.TargetRectangle.bottom = s->height;
544
545 ID3D12VideoProcessCommandList_ProcessFrames(
546 s->command_list,
547 s->video_processor,
548 &output_args,
549 1,
550 &input_args
551 );
552
553 for (int i = 0; i < 2; i++) {
554 FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
555 }
556 ID3D12VideoProcessCommandList_ResourceBarrier(s->command_list, 2, barriers);
557
558 hr = ID3D12VideoProcessCommandList_Close(s->command_list);
559 if (FAILED(hr)) {
560 av_log(ctx, AV_LOG_ERROR, "Failed to close command list: HRESULT 0x%lX\n", hr);
561 ret = AVERROR_EXTERNAL;
562 goto fail;
563 }
564
565 ID3D12CommandList *cmd_lists[] = { (ID3D12CommandList *)s->command_list };
566 ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, cmd_lists);
567
568 hr = ID3D12CommandQueue_Signal(s->command_queue, s->fence, s->fence_value);
569 if (FAILED(hr)) {
570 av_log(ctx, AV_LOG_ERROR, "Failed to signal fence: HRESULT 0x%lX\n", hr);
571 ret = AVERROR_EXTERNAL;
572 goto fail;
573 }
574
575 output_frame->sync_ctx.fence = s->fence;
576 output_frame->sync_ctx.fence_value = s->fence_value;
577 ID3D12Fence_AddRef(s->fence); ///< Increment reference count
578
579 s->fence_value++;
580
581 ret = av_frame_copy_props(out, in);
582 if (ret < 0) {
583 av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n");
584 goto fail;
585 }
586
587 out->width = s->width;
588 out->height = s->height;
589 out->format = AV_PIX_FMT_D3D12;
590
591 av_frame_free(&in);
592
593 return ff_filter_frame(outlink, out);
594
595fail:
596 av_frame_free(&in);
598 return ret;
599}
600
602{
603 AVFilterContext *ctx = outlink->src;
604 ScaleD3D12Context *s = ctx->priv;
605 AVFilterLink *inlink = ctx->inputs[0];
606 FilterLink *inl = ff_filter_link(inlink);
607 FilterLink *outl = ff_filter_link(outlink);
608 int ret;
609
611
612 av_buffer_unref(&s->hw_frames_ctx_out);
613 av_buffer_unref(&s->hw_device_ctx);
614
615 ret = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &s->width, &s->height);
616 if (ret < 0) {
617 av_log(ctx, AV_LOG_ERROR, "Failed to evaluate dimensions\n");
618 return ret;
619 }
620
621 /* Adjust dimensions to meet codec/hardware alignment requirements */
622 ret = ff_scale_adjust_dimensions(inlink, &s->width, &s->height, 0, 1, 1.f);
623 if (ret < 0)
624 return ret;
625
626 outlink->w = s->width;
627 outlink->h = s->height;
628
629 if (!inl->hw_frames_ctx) {
630 av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on input link\n");
631 return AVERROR(EINVAL);
632 }
633
634 if (!s->hw_device_ctx) {
635 AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
636 s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref);
637 if (!s->hw_device_ctx) {
638 av_log(ctx, AV_LOG_ERROR, "Failed to initialize filter hardware device context\n");
639 return AVERROR(ENOMEM);
640 }
641 }
642
643 AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data;
644 AVD3D12VADeviceContext *d3d12_hwctx = (AVD3D12VADeviceContext *)hwctx->hwctx;
645
646 s->device = d3d12_hwctx->device;
647
648 if (!s->device) {
649 av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D12 device\n");
650 return AVERROR(EINVAL);
651 }
652
653 s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx);
654 if (!s->hw_frames_ctx_out)
655 return AVERROR(ENOMEM);
656
657 AVHWFramesContext *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data;
658 AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
659
660 if (s->format == AV_PIX_FMT_NONE) {
661 /* If format is not specified, use the same format as input */
662 frames_ctx->sw_format = in_frames_ctx->sw_format;
663 s->format = in_frames_ctx->sw_format;
664 av_log(ctx, AV_LOG_VERBOSE, "D3D12 scale output format not specified, using input format: %s\n",
665 av_get_pix_fmt_name(s->format));
666 } else {
667 frames_ctx->sw_format = s->format;
668 }
669
670 /* Set output format based on sw_format */
671 switch (frames_ctx->sw_format) {
672 case AV_PIX_FMT_NV12:
673 s->output_format = DXGI_FORMAT_NV12;
674 break;
675 case AV_PIX_FMT_P010:
676 s->output_format = DXGI_FORMAT_P010;
677 break;
678 default:
679 av_log(ctx, AV_LOG_ERROR, "Unsupported output format: %s\n",
680 av_get_pix_fmt_name(frames_ctx->sw_format));
681 av_buffer_unref(&s->hw_frames_ctx_out);
682 return AVERROR(EINVAL);
683 }
684
685 frames_ctx->width = s->width;
686 frames_ctx->height = s->height;
687 frames_ctx->format = AV_PIX_FMT_D3D12;
688 frames_ctx->initial_pool_size = 10;
689
690 if (ctx->extra_hw_frames > 0)
691 frames_ctx->initial_pool_size += ctx->extra_hw_frames;
692
693 AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
694
695 /*
696 * Set D3D12 resource flags for video processing
697 * ALLOW_RENDER_TARGET is needed for video processor output
698 */
699 frames_hwctx->format = s->output_format;
700 frames_hwctx->resource_flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
701 frames_hwctx->heap_flags = D3D12_HEAP_FLAG_NONE;
702
703 ret = av_hwframe_ctx_init(s->hw_frames_ctx_out);
704 if (ret < 0) {
705 av_buffer_unref(&s->hw_frames_ctx_out);
706 return ret;
707 }
708
709 outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out);
710 if (!outl->hw_frames_ctx)
711 return AVERROR(ENOMEM);
712
713 av_log(ctx, AV_LOG_VERBOSE, "D3D12 scale config: %dx%d -> %dx%d\n",
714 inlink->w, inlink->h, outlink->w, outlink->h);
715 return 0;
716}
717
719 ScaleD3D12Context *s = ctx->priv;
720
722
723 av_buffer_unref(&s->hw_frames_ctx_out);
724 av_buffer_unref(&s->hw_device_ctx);
725
726 av_freep(&s->w_expr);
727 av_freep(&s->h_expr);
728}
729
731 {
732 .name = "default",
733 .type = AVMEDIA_TYPE_VIDEO,
734 .filter_frame = scale_d3d12_filter_frame,
735 },
736};
737
739 {
740 .name = "default",
741 .type = AVMEDIA_TYPE_VIDEO,
742 .config_props = scale_d3d12_config_props,
743 },
744};
745
746#define OFFSET(x) offsetof(ScaleD3D12Context, x)
747#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
748
750 { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
751 { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
752 { "format", "Output video pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, INT_MIN, INT_MAX, .flags=FLAGS },
753 { NULL }
754};
755
757
759 .p.name = "scale_d3d12",
760 .p.description = NULL_IF_CONFIG_SMALL("Scale video using Direct3D12"),
761 .priv_size = sizeof(ScaleD3D12Context),
762 .p.priv_class = &scale_d3d12_class,
763 .init = scale_d3d12_init,
764 .uninit = scale_d3d12_uninit,
768 .p.flags = AVFILTER_FLAG_HWDEVICE,
769 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
770};
static const char *const format[]
Definition af_aiir.c:444
const FFFilter ff_vf_scale_d3d12
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
#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:598
#define NULL
Definition coverity.c:32
#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:187
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:139
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:103
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR(e)
Definition error.h:45
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:860
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#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:254
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#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:88
#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:3380
#define AV_PIX_FMT_P010
Definition pixfmt.h:608
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:672
@ AVCOL_TRC_SMPTE2084
SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems.
Definition pixfmt.h:689
@ AVCOL_TRC_ARIB_STD_B67
ARIB STD-B67, known as "Hybrid log-gamma".
Definition pixfmt.h:693
AVColorSpace
YUV colorspace type.
Definition pixfmt.h:706
@ 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:712
@ AVCOL_SPC_BT2020_CL
ITU-R BT2020 constant luminance system.
Definition pixfmt.h:718
@ AVCOL_SPC_BT2020_NCL
ITU-R BT2020 non-constant luminance system.
Definition pixfmt.h:717
@ AVCOL_SPC_SMPTE170M
also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
Definition pixfmt.h:713
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.
An instance of a filter.
Definition avfilter.h:273
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:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
AVBufferRef * hw_frames_ctx
For hwaccel-format frames, this should be a reference to the AVHWFramesContext describing the frame.
Definition frame.h:769
enum AVColorSpace colorspace
YUV colorspace type.
Definition frame.h:734
enum AVColorTransferCharacteristic color_trc
Definition frame.h:727
int64_t duration
Duration of the frame, in the same units as pts.
Definition frame.h:820
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
ID3D12VideoProcessor * video_processor
AVBufferRef * hw_device_ctx
ID3D12VideoDevice * video_device
DXGI_FORMAT output_format
enum AVPixelFormat format
ID3D12CommandAllocator * command_allocator
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 FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
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[]
#define OFFSET(x)
static DXGI_COLOR_SPACE_TYPE get_dxgi_colorspace(enum AVColorSpace colorspace, enum AVColorTransferCharacteristic trc, int is_10bit)
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