1 /*****************************************************************************
2 * d3d11_deinterlace.c: D3D11 deinterlacing filter
3 *****************************************************************************
4 * Copyright (C) 2017 Videolabs SAS
6 * Authors: Steve Lhomme <robux4@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_filter.h>
32 #include <vlc_picture.h>
38 #include "d3d11_filters.h"
39 #include "../../video_chroma/d3d11_fmt.h"
40 #include "../../video_filter/deinterlace/common.h"
43 typedef UINT D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS
;
44 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND 0x1
45 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB 0x2
46 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE 0x4
47 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION 0x8
48 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE 0x10
49 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION 0x20
54 d3d11_device_t d3d_dev
;
55 ID3D11VideoDevice
*d3dviddev
;
56 ID3D11VideoContext
*d3dvidctx
;
57 ID3D11VideoProcessor
*videoProcessor
;
58 ID3D11VideoProcessorEnumerator
*procEnumerator
;
62 ID3D11Texture2D
*outTexture
;
63 ID3D11Resource
*outResource
;
65 ID3D11VideoProcessorOutputView
*processorOutput
;
67 struct deinterlace_ctx context
;
68 picture_t
* (*buffer_new
)( filter_t
* );
74 D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS i_mode
;
75 deinterlace_algo settings
;
77 static struct filter_mode_t filter_mode
[] = {
78 { "blend", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND
,
79 { false, false, false, false } },
80 { "bob", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB
,
81 { true, false, false, false } },
82 { "x", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION
,
83 { true, true, false, false } },
84 { "ivtc", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE
,
85 { false, true, true, false } },
86 { "yadif2x", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE
,
87 { true, true, false, false } },
90 static int assert_ProcessorInput(filter_t
*p_filter
, picture_sys_t
*p_sys_src
)
92 filter_sys_t
*p_sys
= p_filter
->p_sys
;
93 if (!p_sys_src
->processorInput
)
95 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc
= {
97 .ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
,
98 .Texture2D
.MipSlice
= 0,
99 .Texture2D
.ArraySlice
= p_sys_src
->slice_index
,
103 hr
= ID3D11VideoDevice_CreateVideoProcessorInputView(p_sys
->d3dviddev
,
104 p_sys_src
->resource
[KNOWN_DXGI_INDEX
],
105 p_sys
->procEnumerator
,
107 &p_sys_src
->processorInput
);
111 msg_Dbg(p_filter
,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys_src
->slice_index
, hr
);
119 static void Flush(filter_t
*filter
)
121 FlushDeinterlacing(&filter
->p_sys
->context
);
124 static int RenderPic( filter_t
*p_filter
, picture_t
*p_outpic
, picture_t
*p_pic
,
125 int order
, int i_field
)
129 filter_sys_t
*p_sys
= p_filter
->p_sys
;
131 picture_t
*p_prev
= p_sys
->context
.pp_history
[0];
132 picture_t
*p_cur
= p_sys
->context
.pp_history
[1];
133 picture_t
*p_next
= p_sys
->context
.pp_history
[2];
135 /* TODO adjust the format if it's the first or second field ? */
136 D3D11_VIDEO_FRAME_FORMAT frameFormat
= !i_field
?
137 D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST
:
138 D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST
;
140 ID3D11VideoContext_VideoProcessorSetStreamFrameFormat(p_sys
->d3dvidctx
, p_sys
->videoProcessor
, 0, frameFormat
);
142 D3D11_VIDEO_PROCESSOR_STREAM stream
= {0};
143 stream
.Enable
= TRUE
;
144 stream
.InputFrameOrField
= i_field
? 1 : 0;
146 if( p_cur
&& p_next
)
148 picture_sys_t
*picsys_next
= ActivePictureSys(p_next
);
149 if ( assert_ProcessorInput(p_filter
, picsys_next
) )
152 picture_sys_t
*picsys_cur
= ActivePictureSys(p_cur
);
153 if ( assert_ProcessorInput(p_filter
, picsys_cur
) )
158 picture_sys_t
*picsys_prev
= ActivePictureSys(p_prev
);
159 if ( assert_ProcessorInput(p_filter
, picsys_prev
) )
162 stream
.pInputSurface
= picsys_cur
->processorInput
;
163 stream
.ppFutureSurfaces
= &picsys_next
->processorInput
;
164 stream
.ppPastSurfaces
= &picsys_prev
->processorInput
;
166 stream
.PastFrames
= 1;
167 stream
.FutureFrames
= 1;
171 /* p_next is the current, p_cur is the previous frame */
172 stream
.pInputSurface
= picsys_next
->processorInput
;
173 stream
.ppPastSurfaces
= &picsys_cur
->processorInput
;
174 stream
.PastFrames
= 1;
179 picture_sys_t
*p_sys_src
= ActivePictureSys(p_pic
);
180 if ( assert_ProcessorInput(p_filter
, p_sys_src
) )
183 /* first single frame */
184 stream
.pInputSurface
= p_sys_src
->processorInput
;
190 srcRect
.right
= p_pic
->format
.i_visible_width
;
191 srcRect
.bottom
= p_pic
->format
.i_visible_height
;
192 ID3D11VideoContext_VideoProcessorSetStreamSourceRect(p_sys
->d3dvidctx
, p_sys
->videoProcessor
,
194 ID3D11VideoContext_VideoProcessorSetStreamDestRect(p_sys
->d3dvidctx
, p_sys
->videoProcessor
,
197 hr
= ID3D11VideoContext_VideoProcessorBlt(p_sys
->d3dvidctx
, p_sys
->videoProcessor
,
198 p_sys
->processorOutput
,
205 .bottom
= p_outpic
->format
.i_visible_height
,
207 .right
= p_outpic
->format
.i_visible_width
,
211 ID3D11DeviceContext_CopySubresourceRegion(p_outpic
->p_sys
->context
,
212 p_outpic
->p_sys
->resource
[KNOWN_DXGI_INDEX
],
213 p_outpic
->p_sys
->slice_index
,
220 static int RenderSinglePic( filter_t
*p_filter
, picture_t
*p_outpic
, picture_t
*p_pic
)
222 return RenderPic( p_filter
, p_outpic
, p_pic
, 0, 0 );
225 static picture_t
*Deinterlace(filter_t
*p_filter
, picture_t
*p_pic
)
227 filter_sys_t
*p_sys
= p_filter
->p_sys
;
229 if( p_sys
->context_mutex
!= INVALID_HANDLE_VALUE
)
230 WaitForSingleObjectEx( p_sys
->context_mutex
, INFINITE
, FALSE
);
232 picture_t
*res
= DoDeinterlacing( p_filter
, &p_sys
->context
, p_pic
);
234 if( p_sys
->context_mutex
!= INVALID_HANDLE_VALUE
)
235 ReleaseMutex( p_sys
->context_mutex
);
240 static const struct filter_mode_t
*GetFilterMode(const char *mode
)
242 if ( mode
== NULL
|| !strcmp( mode
, "auto" ) )
245 for (size_t i
=0; i
<ARRAY_SIZE(filter_mode
); i
++)
247 if( !strcmp( mode
, filter_mode
[i
].psz_mode
) )
248 return &filter_mode
[i
];
253 static void d3d11_pic_context_destroy(struct picture_context_t
*opaque
)
255 struct va_pic_context
*pic_ctx
= (struct va_pic_context
*)opaque
;
256 ReleasePictureSys(&pic_ctx
->picsys
);
260 static struct picture_context_t
*d3d11_pic_context_copy(struct picture_context_t
*ctx
)
262 struct va_pic_context
*src_ctx
= (struct va_pic_context
*)ctx
;
263 struct va_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
264 if (unlikely(pic_ctx
==NULL
))
266 pic_ctx
->s
.destroy
= d3d11_pic_context_destroy
;
267 pic_ctx
->s
.copy
= d3d11_pic_context_copy
;
268 pic_ctx
->picsys
= src_ctx
->picsys
;
269 AcquirePictureSys(&pic_ctx
->picsys
);
273 static picture_t
*NewOutputPicture( filter_t
*p_filter
)
275 picture_t
*pic
= p_filter
->p_sys
->buffer_new( p_filter
);
278 bool b_local_texture
= false;
282 pic
->p_sys
= calloc(1, sizeof(*pic
->p_sys
));
283 if (unlikely(pic
->p_sys
== NULL
))
286 D3D11_TEXTURE2D_DESC dstDesc
;
287 ID3D11Texture2D_GetDesc(p_filter
->p_sys
->outTexture
, &dstDesc
);
289 const d3d_format_t
*cfg
= NULL
;
290 for (const d3d_format_t
*output_format
= GetRenderFormatList();
291 output_format
->name
!= NULL
; ++output_format
)
293 if (output_format
->formatTexture
== dstDesc
.Format
&&
294 is_d3d11_opaque(output_format
->fourcc
))
300 if (unlikely(cfg
== NULL
))
306 /* create the texture that's missing */
307 video_format_t fmt
= p_filter
->fmt_out
.video
;
308 fmt
.i_width
= dstDesc
.Width
;
309 fmt
.i_height
= dstDesc
.Height
;
310 if (AllocateTextures(VLC_OBJECT(p_filter
), &p_filter
->p_sys
->d3d_dev
, cfg
,
311 &fmt
, 1, pic
->p_sys
->texture
) != VLC_SUCCESS
)
316 b_local_texture
= true;
318 pic
->p_sys
->context
= p_filter
->p_sys
->d3d_dev
.d3dcontext
;
319 pic
->p_sys
->formatTexture
= dstDesc
.Format
;
322 /* the picture might be duplicated for snapshots so it needs a context */
323 struct va_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
324 if (likely(pic_ctx
!=NULL
))
326 pic_ctx
->s
.destroy
= d3d11_pic_context_destroy
;
327 pic_ctx
->s
.copy
= d3d11_pic_context_copy
;
328 pic_ctx
->picsys
= *pic
->p_sys
;
329 AcquirePictureSys( &pic_ctx
->picsys
);
330 pic
->context
= &pic_ctx
->s
;
332 if (b_local_texture
) {
333 for (int i
=0; i
<D3D11_MAX_SHADER_VIEW
; i
++) {
334 if (pic
->p_sys
->texture
[i
])
335 ID3D11Texture2D_Release(pic
->p_sys
->texture
[i
]);
342 int D3D11OpenDeinterlace(vlc_object_t
*obj
)
344 filter_t
*filter
= (filter_t
*)obj
;
346 ID3D11VideoProcessorEnumerator
*processorEnumerator
= NULL
;
348 if (!is_d3d11_opaque(filter
->fmt_in
.video
.i_chroma
))
350 if (!video_format_IsSimilar(&filter
->fmt_in
.video
, &filter
->fmt_out
.video
))
353 filter_sys_t
*sys
= malloc(sizeof (*sys
));
354 if (unlikely(sys
== NULL
))
356 memset(sys
, 0, sizeof (*sys
));
358 D3D11_TEXTURE2D_DESC dstDesc
;
359 D3D11_FilterHoldInstance(filter
, &sys
->d3d_dev
, &dstDesc
);
360 if (unlikely(sys
->d3d_dev
.d3dcontext
==NULL
))
362 msg_Dbg(filter
, "Filter without a context");
367 hr
= ID3D11Device_QueryInterface(sys
->d3d_dev
.d3ddevice
, &IID_ID3D11VideoDevice
, (void **)&sys
->d3dviddev
);
369 msg_Err(filter
, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr
);
373 hr
= ID3D11DeviceContext_QueryInterface(sys
->d3d_dev
.d3dcontext
, &IID_ID3D11VideoContext
, (void **)&sys
->d3dvidctx
);
375 msg_Err(filter
, "Could not Query ID3D11VideoContext Interface from the picture. (hr=0x%lX)", hr
);
379 HANDLE context_lock
= INVALID_HANDLE_VALUE
;
380 UINT dataSize
= sizeof(context_lock
);
381 hr
= ID3D11Device_GetPrivateData(sys
->d3d_dev
.d3ddevice
, &GUID_CONTEXT_MUTEX
, &dataSize
, &context_lock
);
383 msg_Warn(filter
, "No mutex found to lock the decoder");
384 sys
->context_mutex
= context_lock
;
386 const video_format_t
*fmt
= &filter
->fmt_out
.video
;
388 D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc
= {
389 .InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST
,
391 .Numerator
= fmt
->i_frame_rate
,
392 .Denominator
= fmt
->i_frame_rate_base
,
394 .InputWidth
= fmt
->i_width
,
395 .InputHeight
= fmt
->i_height
,
396 .OutputWidth
= dstDesc
.Width
,
397 .OutputHeight
= dstDesc
.Height
,
399 .Numerator
= fmt
->i_frame_rate
,
400 .Denominator
= fmt
->i_frame_rate_base
,
402 .Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
,
404 hr
= ID3D11VideoDevice_CreateVideoProcessorEnumerator(sys
->d3dviddev
, &processorDesc
, &processorEnumerator
);
405 if ( processorEnumerator
== NULL
)
407 msg_Dbg(filter
, "Can't get a video processor for the video.");
413 D3D11_LogProcessorSupport(filter
, processorEnumerator
);
415 hr
= ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator
, dstDesc
.Format
, &flags
);
418 msg_Dbg(filter
, "can't read processor support for %s", DxgiFormatToStr(dstDesc
.Format
));
421 if ( !(flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
) ||
422 !(flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
) )
424 msg_Dbg(filter
, "deinterlacing %s is not supported", DxgiFormatToStr(dstDesc
.Format
));
428 D3D11_VIDEO_PROCESSOR_CAPS processorCaps
;
429 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(processorEnumerator
, &processorCaps
);
433 char *psz_mode
= var_InheritString( filter
, "deinterlace-mode" );
434 const struct filter_mode_t
*p_mode
= GetFilterMode(psz_mode
);
437 msg_Dbg(filter
, "unknown mode %s, trying blend", psz_mode
);
438 p_mode
= GetFilterMode("blend");
440 if (strcmp(p_mode
->psz_mode
, psz_mode
))
441 msg_Dbg(filter
, "using %s deinterlacing mode", p_mode
->psz_mode
);
443 D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rateCaps
;
444 for (UINT type
= 0; type
< processorCaps
.RateConversionCapsCount
; ++type
)
446 ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps(processorEnumerator
, type
, &rateCaps
);
447 if (!(rateCaps
.ProcessorCaps
& p_mode
->i_mode
))
450 hr
= ID3D11VideoDevice_CreateVideoProcessor(sys
->d3dviddev
,
451 processorEnumerator
, type
, &sys
->videoProcessor
);
454 sys
->videoProcessor
= NULL
;
456 if ( sys
->videoProcessor
==NULL
&&
457 p_mode
->i_mode
!= D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB
)
459 msg_Dbg(filter
, "mode %s not available, trying bob", psz_mode
);
460 p_mode
= GetFilterMode("bob");
461 for (UINT type
= 0; type
< processorCaps
.RateConversionCapsCount
; ++type
)
463 ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps(processorEnumerator
, type
, &rateCaps
);
464 if (!(rateCaps
.ProcessorCaps
& D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB
))
467 hr
= ID3D11VideoDevice_CreateVideoProcessor(sys
->d3dviddev
,
468 processorEnumerator
, type
, &sys
->videoProcessor
);
471 sys
->videoProcessor
= NULL
;
475 if (sys
->videoProcessor
== NULL
)
477 msg_Dbg(filter
, "couldn't find a deinterlacing filter");
481 D3D11_TEXTURE2D_DESC texDesc
;
482 ZeroMemory(&texDesc
, sizeof(texDesc
));
483 texDesc
.MipLevels
= 1;
484 texDesc
.SampleDesc
.Count
= 1;
485 texDesc
.MiscFlags
= 0; //D3D11_RESOURCE_MISC_SHARED;
486 texDesc
.Usage
= D3D11_USAGE_DEFAULT
;
487 texDesc
.CPUAccessFlags
= 0;
488 texDesc
.Format
= dstDesc
.Format
;
489 texDesc
.BindFlags
= D3D11_BIND_RENDER_TARGET
;
490 texDesc
.Usage
= D3D11_USAGE_DEFAULT
;
491 texDesc
.CPUAccessFlags
= 0;
492 texDesc
.ArraySize
= 1;
493 texDesc
.Height
= dstDesc
.Height
;
494 texDesc
.Width
= dstDesc
.Width
;
496 hr
= ID3D11Device_CreateTexture2D( sys
->d3d_dev
.d3ddevice
, &texDesc
, NULL
, &sys
->outTexture
);
498 msg_Err(filter
, "CreateTexture2D failed. (hr=0x%0lx)", hr
);
502 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc
= {
503 .ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
,
504 .Texture2D
.MipSlice
= 0,
507 hr
= ID3D11VideoDevice_CreateVideoProcessorOutputView(sys
->d3dviddev
,
511 &sys
->processorOutput
);
514 msg_Dbg(filter
,"Failed to create processor output. (hr=0x%lX)", hr
);
518 sys
->procEnumerator
= processorEnumerator
;
520 InitDeinterlacingContext( &sys
->context
);
522 sys
->context
.settings
= p_mode
->settings
;
523 sys
->context
.settings
.b_use_frame_history
= rateCaps
.PastFrames
!= 0 ||
524 rateCaps
.FutureFrames
!= 0;
525 if (sys
->context
.settings
.b_use_frame_history
!= p_mode
->settings
.b_use_frame_history
)
526 msg_Dbg(filter
, "deinterlacing not using frame history as requested");
527 if (sys
->context
.settings
.b_double_rate
)
528 sys
->context
.pf_render_ordered
= RenderPic
;
530 sys
->context
.pf_render_single_pic
= RenderSinglePic
;
532 video_format_t out_fmt
;
533 GetDeinterlacingOutput( &sys
->context
, &out_fmt
, &filter
->fmt_in
.video
);
534 if( !filter
->b_allow_fmt_out_change
&&
535 out_fmt
.i_height
!= filter
->fmt_in
.video
.i_height
)
540 sys
->buffer_new
= filter
->owner
.video
.buffer_new
;
541 filter
->owner
.video
.buffer_new
= NewOutputPicture
;
542 filter
->fmt_out
.video
= out_fmt
;
543 filter
->pf_video_filter
= Deinterlace
;
544 filter
->pf_flush
= Flush
;
550 ID3D11Texture2D_Release(sys
->outTexture
);
551 if (sys
->videoProcessor
)
552 ID3D11VideoProcessor_Release(sys
->videoProcessor
);
553 if (processorEnumerator
)
554 ID3D11VideoProcessorEnumerator_Release(processorEnumerator
);
556 ID3D11VideoContext_Release(sys
->d3dvidctx
);
558 ID3D11VideoDevice_Release(sys
->d3dviddev
);
559 if (sys
->d3d_dev
.d3dcontext
)
560 D3D11_FilterReleaseInstance(&sys
->d3d_dev
);
566 void D3D11CloseDeinterlace(vlc_object_t
*obj
)
568 filter_t
*filter
= (filter_t
*)obj
;
569 filter_sys_t
*sys
= filter
->p_sys
;
571 ID3D11VideoProcessorOutputView_Release(sys
->processorOutput
);
572 ID3D11Texture2D_Release(sys
->outTexture
);
573 ID3D11VideoProcessor_Release(sys
->videoProcessor
);
574 ID3D11VideoProcessorEnumerator_Release(sys
->procEnumerator
);
575 ID3D11VideoContext_Release(sys
->d3dvidctx
);
576 ID3D11VideoDevice_Release(sys
->d3dviddev
);
577 D3D11_FilterReleaseInstance(&sys
->d3d_dev
);